Author: Matteo Chiacchia
Prof. Roberto Monte

L’obiettivo dello studio è effettuare il forecast della serie storica relativa alle vendite mensili della Toyota Camry negli USA. Per raggiungere l’obiettivo desiderato, sono stati utilizzate delle tecniche che, a partire dall’analisi accurata dei dati passati, sono in grado di creare un modello di previsione che sia più o meno accurato. In particolare sono stati utilizzati:

Modello STL

L’algoritmo STL (Seasonal and Trend decomposition using Loess) utilizza la tecnica di smoothing LOESS per analizzare una serie temporale. Esso esegue due cicli di smoothing: uno interno che itera tra lo smoothing stagionale e quello di trend e uno esterno che riduce al minimo l’effetto degli outlier durante il processo di smoothing interno. Nel primo ciclo, viene calcolata la componente stagionale e rimossa per calcolare la componente di trend. Nel secondo ciclo, le componenti stagionali e di trend vengono sottratte dalla serie temporale per ottenere il remainder. In questo modo, l’algoritmo STL suddivide la serie temporale in tre componenti principali: la componente stagionale, la componente di trend e il remainder, che può essere interpretato come il noise residuo. È possibile applicare la decomposizione STL a qualsiasi set di dati, ma risultati significativi vengono ottenuti solo se nei dati esiste una componente stagionale.

Rispetto ad altre metodologie:

Time series analysis and forecast with STL

Lo svolgimento dello studio effettuato è classificabile in due macro categorie:

1. Analisi

Il primo passaggio è caricare le librerie di riferimento

library(base)
library(stats)
library(astsa)
library(tibble)
library(dplyr)
library(readxl)
library(numbers)
library(ggplot2)
library(EnvStats)
library(DescTools)
library(lattice)
library(leaps)
library(ltsa)
library(bestglm)
library(zoo)
library(lmtest)
library(forecast)
library(gridExtra)
library(grid)
library(gtable)
library(tsibble)
library(fabletools)
library(fable)
library(feasts)
library(crayon)
library(fBasics)
library(nortest)
library(tseries)
library(survival)
library(MASS)
library(fitdistrplus)
library(glogis)
library(car)
library(pracma)
library(NlcOptim)
library(goftest)
library(qqplotr)
library(BiocManager)
library(stats4)
library(dynamicTreeCut)
library(fastcluster)
library(xts)
library(TTR)
library(quantmod)
library(urca)
library(fpp3)
graphics.off()
# Removes all items in Environment!
rm(list=ls())
# To reset options to default values
def_options <- options()
# Sets the data directory.
WD <- dirname(rstudioapi::getSourceEditorContext()$path)
show(WD)
[1] "/Users/chiacchius/Desktop/MPSMF_project"
setwd(WD)
dir()
 [1] "Camry_BTC_log_gran_mean_point_resid_df.csv" "Camry_BTC_log_HW_point_resid_df.csv"       
 [3] "Camry_BTC_log_naive_point_resid_df.csv"     "Camry_BTC_log_stl_point_resid_df.csv"      
 [5] "Camry_US_sales_testset.csv"                 "Camry_US_sales_trainset.csv"               
 [7] "Camry_US_sales.xlsx"                        "ETS_model.nb.html"                         
 [9] "ETS_model.Rmd"                              "sales_log_stl_point_pred_df"               
[11] "STL_model.nb.html"                          "STL_model.Rmd"                             
#
# To clear the console
cat("\014")

# Functions
nextodd <- function(x){
  x <- round(x)
  if(x%%2==0) x <- x+1
  as.integer(x)
}

I dati relativi alle vendite mensili della Toyota Camry negli USA sono salvati all’interno del file ExcelCamry_US_sales.xlsx”. Viene effettuato, quindi, il caricamento del file Excel e i dati vengono salvati come Dataframe.

df_Camry_US_sales <- read_excel("Camry_US_sales.xlsx")
tail(df_Camry_US_sales)

Da notare che il numero di vendite relative a Dicembre 2022 è uguale a 0, di conseguenza si è deciso di eliminare questo dato e considerare come ultimo elemento del dataframe quello risalente al mese precedente (Novembre 2022)

#delete last row of df because it has not value
df_Camry_US_sales <- df_Camry_US_sales[-nrow(df_Camry_US_sales), ]
tail(df_Camry_US_sales)
head(df_Camry_US_sales)

I primi dati risalgono a Gennaio 2005, ma si è deciso di eseguire l’analisi a partire dai dati del 2009 per evitare che l’influenza dei valori di mercato troppo lontani temporalmente possa avere effetto negativo durante il processo di analisi e forecasting. Aggiungiamo inoltre una colonna (t) di indice al dataframe, che replica semplicemente i numeri di riga ed è utile per scopi di tracciamento.

df_Camry_US_sales$date <- as.Date(df_Camry_US_sales$date)
#time series start = 01/2009
df_Camry_US_sales <- df_Camry_US_sales[df_Camry_US_sales$date >= "2009-01-31", ]
df <- data.frame(Year=year(df_Camry_US_sales$date), Month=month(df_Camry_US_sales$date), date=df_Camry_US_sales$date,
                 sales=as.vector(df_Camry_US_sales$sales))

df <- add_column(add_column(df, t=1:nrow(df), .before="Year"))
Data_df <- df
Data_df <- dplyr::rename(Data_df, x=t, y=sales)
head(df)

Per effettuare analisi e predizione si è deciso di suddividere il dataframe in due parti: TrainSet e TestSet. La decisione presa è quella di suddividere il dataframe in modo tale da avere nel TestSet 12 mesi e poter quindi eseguire la predizione su questi (predizione mensile dell’intero anno successivo), utlizzando come Time Series i valori presenti nel TrainSet per creare il modello.

#parameters
DS_length <- nrow(Data_df)
TrnS_length <- floor(DS_length*0.93)
TstS_length <- DS_length-TrnS_length
df_string <- sprintf("Data Frame lenght: %d\n", DS_length)
trn_string <- sprintf("Train Set lenght: %d\n", TrnS_length)
tst_string <- sprintf("Test Set lenght: %d\n", TstS_length)
cat(df_string)
Data Frame lenght: 167
cat(trn_string)
Train Set lenght: 155
cat(tst_string)
Test Set lenght: 12

Si visualizzano inizialmente i dati tramite lo Scatter Plot

# We draw the scatter plot.
# We set the initial and final date of the time series. These dates enter the Title of the plot.
First_Date <- paste(Data_df$Month[1],Data_df$Year[1])
Last_Date <- paste(Data_df$Month[DS_length],Data_df$Year[DS_length])
# In case the dates are contained in a single column, *Date*, we use the following syntax.
# First_Date <- Data_df$Date[1] 
# Last_Date <- Data_df$Date[DS_length]
# We set the scatter plot title, the subtitle and the author.
title_content <- bquote(atop("Scatter Plot of the Monthly Toyota Camry sales in the U.S. - Training and Test Sets - from ", .(First_Date), " to ", .(Last_Date)))
subtitle_content <- bquote(paste("Training set length ", .(TrnS_length), " sample points. Test set length ", .(TstS_length), " sample points."))
caption_content <- "Author: Matteo Chiacchia"
# We set the x-axis name. However, in this case, since dates will mark the x-axis ticks, we think that it is unnecessary to give the x-axis a name.
x_name <- bquote("")
# To obtain the sub-multiples of the length of the data set as a hint on the number of breaks to use, we factorize the length of the time series.
# library(numbers)
# primeFactors(DS_length)
x_breaks_num <- 33
x_breaks_min <- Data_df$x[1]
x_breaks_max <- Data_df$x[DS_length]
# x_binwidth <- floor((x_breaks_max-x_breaks_min)/x_breaks_num)
x_binwidth <- floor(DS_length/x_breaks_num)
x_breaks <- seq(from=x_breaks_min, to=x_breaks_max, by=x_binwidth)
if((x_breaks_max-max(x_breaks))>x_binwidth/2){x_breaks <- c(x_breaks,x_breaks_max)}
x_labs <- paste(Data_df$Month[x_breaks],Data_df$Year[x_breaks])
J <- 0.0
x_lims <- c(x_breaks_min-J*x_binwidth, x_breaks_max+J*x_binwidth)
y_name <- bquote("Monthly Sales")
y_breaks_num <- 10
y_max <- max(na.omit(Data_df$y))
y_min <- min(na.omit(Data_df$y))
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_min <- y_min-y_binwidth
y_breaks_max <- y_max+y_binwidth
y_breaks <- round(seq(from=y_breaks_min, to=y_breaks_max, by=y_binwidth),3)
# y_breaks_low <- floor((y_min/y_binwidth))*y_binwidth
# y_breaks_up <- ceiling((y_max/y_binwidth))*y_binwidth
# y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0.0
y_lims <- c((y_breaks_min-K*y_binwidth), (y_breaks_max+K*y_binwidth))
# y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
col_k <- bquote("Training set")
col_b <- bquote("Test set")
col_g <- bquote("Regression line (training set)")
col_r <- bquote("LOESS curve (training set)")
leg_labs   <- c(col_k, col_b, col_g, col_r)
leg_cols   <- c("col_k"="black", "col_b"="blue", "col_r"="red", "col_g"="green")
leg_breaks <- c("col_k", "col_b", "col_g", "col_r")
# library(ggplot2)
df_sp <- ggplot(Data_df) +
  geom_vline(xintercept=Data_df$x[TrnS_length], linewidth=0.3, colour="black") +
  geom_smooth(data=subset(Data_df, Data_df$x <= x[TrnS_length]), alpha=1, linewidth=0.7, linetype="solid", 
              aes(x=x, y=y, color="col_g"), method="lm", formula=y~x, se=FALSE, fullrange=FALSE) +
  geom_smooth(data=subset(Data_df, Data_df$x <= x[TrnS_length]), alpha=1, linewidth=0.7, linetype="dashed", 
              aes(x=x, y=y, color="col_r"), method="loess", formula=y~x, se=FALSE) +
  geom_point(data=subset(Data_df, Data_df$x <= x[TrnS_length]), alpha=1, size=0.5, shape=19, 
             aes(x=x, y=y, color="col_k")) +
  geom_point(data=subset(Data_df, Data_df$x > x[TrnS_length]), alpha=1, size=0.5, shape=19, 
             aes(x=x, y=y, color="col_b")) +
  scale_x_continuous(name=x_name, breaks=x_breaks, label=x_labs, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis=sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  scale_colour_manual(name="Legend", labels=leg_labs, values=leg_cols, breaks=leg_breaks,
                      guide=guide_legend(override.aes=list(shape=c(19,19,NA,NA), 
                                                           linetype=c("blank", "blank", "solid", "dashed")))) +
  theme(plot.title=element_text(hjust=0.5),
        plot.subtitle=element_text(hjust= 0.5),
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(df_sp)

e tramite il Line Plot

title_content <- bquote(atop("Line Plot of the Monthly Toyota Camry sales in the U.S. - Training and Test Sets - from ", .(First_Date), " to ", .(Last_Date)))
# We draw the line plot.
df_lp <- ggplot(Data_df) +
  geom_vline(xintercept=Data_df$x[TrnS_length], linewidth=0.3, colour="black") +
  geom_smooth(data=subset(Data_df, Data_df$x <= x[TrnS_length]), alpha=1, linewidth=0.7, linetype="solid", 
              aes(x=x, y=y, color="col_g"), method="lm", formula=y~x, se=FALSE, fullrange=FALSE) +
  geom_smooth(data=subset(Data_df, Data_df$x <= x[TrnS_length]), alpha=1, linewidth=0.7, linetype="dashed", 
              aes(x=x, y=y, color="col_r"), method="loess", formula=y~x, se=FALSE) +
  geom_line(data=subset(Data_df, Data_df$x <= x[TrnS_length]), alpha=1, linewidth=0.5, linetype="solid", 
            aes(x=x, y=y, color="col_k", group=1)) +
  geom_line(data=subset(Data_df, Data_df$x >= x[TrnS_length]), alpha=1, linewidth=0.5, linetype="solid", 
            aes(x=x, y=y, color="col_b", group=1)) +
  scale_x_continuous(name=x_name, breaks=x_breaks, label=x_labs, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis=sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  scale_colour_manual(name="Legend", labels=leg_labs, values=leg_cols, breaks=leg_breaks,
                      guide=guide_legend(override.aes=list(linetype=c("solid", "solid", "solid", "dashed")))) +
  theme(plot.title=element_text(hjust=0.5),
        plot.subtitle=element_text(hjust=0.5),
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(df_lp)

Da un’iniziale ispezione visiva dei grafici si nota come la serie storica non abbia un andamento lineare nel tempo. Si può supporre una presenza di una componente stagionale e di un trend che inizialmente è crescente per diventare in seguito decrescente. Non sembra essere presente un’elevata eteroschedacità, essendo lo spread del path della serie storica all’incirca sempre simile. Questi studi, comunque, verranno effettuati in seguito.

Linear Model

L’idea iniziale è quella di considerare un semplice predittore lineare.

Camry_lm <- lm(df$sales[1:TrnS_length]~t[1:TrnS_length], data=df)
Camry_lm_summ <- summary(Camry_lm)
show(Camry_lm_summ)

Call:
lm(formula = df$sales[1:TrnS_length] ~ t[1:TrnS_length], data = df)

Residuals:
   Min     1Q Median     3Q    Max 
-20509  -3653   -153   3897  22720 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)      31825.88    1067.94  29.801   <2e-16 ***
t[1:TrnS_length]   -18.74      11.88  -1.578    0.117    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6616 on 153 degrees of freedom
Multiple R-squared:  0.01602,   Adjusted R-squared:  0.009588 
F-statistic: 2.491 on 1 and 153 DF,  p-value: 0.1166

Per verificare la correttezza del modello lineare si analizzano i suoi residui e si studiano:

  • Omoschedasticità: significa che i residui sono equamente distribuiti lungo la linea di regressione, ovvero sopra e sotto la linea di regressione e la varianza dei residui dovrebbe essere la stessa per tutti i punteggi previsti lungo la linea di regressione.
  • Assenza di autocorrelazione: L’autocorrelazione si verifica quando i residui non sono indipendenti l’uno dall’altro.
  • Stazionarietà: un modello di predizione con residui stazionari garantisce che le previsioni future siano affidabili e non influenzate da fluttuazioni casuali o tendenze temporali.
  • Gaussianità: se i residui seguono una distribuzione normale.
Camry_lm_fit <- Camry_lm[["fitted.values"]]   
Camry_lm_res <- Camry_lm[["residuals"]]       
Camry_degfr <- Camry_lm[["df.residual"]]      
y_res <- as.vector(Camry_lm_res)
plot(Camry_lm,1)

In questo caso, dal grafico “Residuals vs Fitted”, abbiamo una prova visiva della non stazionarietà media dei residui, il che significa che il modello lineare non sembra in grado di spiegare il trend. D’altro canto, la prova visiva dell’omoschedasticità è confermata: la diffusione dei residui intorno alla linea LOESS non sembra aumentare.

plot(Camry_lm,2)

Dal Q-Q plot si ha una forte prova visiva della non gaussianità dei residui del modello lineare.

Per testare computazionalmente la stazionarietà dei residui, si utilizzano solitamente due test: il test di Augmented Dickey-Fuller (ADF) e il test di Kwiatowski-Phillips-Schmidt-Shin (KPSS). Il test ADF assume l’ipotesi nulla di non stazionarietà. In modo più specifico, il test ADF assume che la serie temporale sia generata da un processo stocastico con un componente di random walk.Al contrario, il test KPSS assume l’ipotesi nulla di stazionarietà. Infatti, il test KPSS assume che la serie temporale sia generata da un processo autoregressivo. Quando il test ADF respinge l’ipotesi nulla e KPSS no, abbiamo prove di stazionarietà nella serie temporale. Quando il test ADF non respinge l’ipotesi nulla e KPSS sì, abbiamo prove di non stazionarietà. Altri casi sono considerati dubbi.

DF test:

 y <- Camry_lm[["residuals"]]   # The data set to be tested.
num_lags <- 0                   # Setting the lag parameter for the test.
Camry_lm_res_DF_none <- ur.df(y, type="none", lags=num_lags, selectlags="Fixed")    
summary(Camry_lm_res_DF_none)   

############################################### 
# Augmented Dickey-Fuller Test Unit Root Test # 
############################################### 

Test regression none 


Call:
lm(formula = z.diff ~ z.lag.1 - 1)

Residuals:
     Min       1Q   Median       3Q      Max 
-17833.1  -2792.5   -100.4   3552.4  21732.7 

Coefficients:
        Estimate Std. Error t value Pr(>|t|)    
z.lag.1 -0.56680    0.07267  -7.799 8.99e-13 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5905 on 153 degrees of freedom
Multiple R-squared:  0.2845,    Adjusted R-squared:  0.2798 
F-statistic: 60.83 on 1 and 153 DF,  p-value: 8.995e-13


Value of test-statistic is: -7.7992 

Critical values for test statistics: 
      1pct  5pct 10pct
tau1 -2.58 -1.95 -1.62

La statistica del test DF assume un valore all’interno della regione di rigetto al livello di significatività α=0,01 o α=1%. Pertanto, possiamo rigettare l’ipotesi nulla in favore dell’alternativa di stazionarietà media.

KPSS test:

y <- Camry_lm[["residuals"]]    # The data set to be tested
Camry_lm_res_KPSS_mu <- ur.kpss(y, type="mu", lags="nil", use.lag=NULL)    
summary(Camry_lm_res_KPSS_mu)    # Showing the result of the test

####################### 
# KPSS Unit Root Test # 
####################### 

Test is of type: mu with 0 lags. 

Value of test-statistic is: 0.9024 

Critical value for a significance level of: 
                10pct  5pct 2.5pct  1pct
critical values 0.347 0.463  0.574 0.739

Il valore della statistica del test KPSS è maggiore del valore critico per il livello di significatività dell’1%, il che significa che dobbiamo rigettare l’ipotesi nulla di stazionarietà media. Questo suggerisce che la serie temporale è probabilmente non stazionaria. I test effettuati non ci danno una certezza evidente della stazionarietà media dei residui, di conseguenza il modello lineare non può essere considerato attendibile.

I test computazionali che vengono solitamente applicati per rilevare l’eteroschedasticità nelle serie temporali sono i test di Breusch-Pagan (BP) e White (W).

Abbiamo: - ipotesi nulla - varianze uguali/costanti nei termini di errore; - ipotesi alternativa - varianze non uguali/non costanti nei termini di errore.

BP e W sono test \(χ^2\). Più alto è il valore di \(χ^2\), equivalentemente più basso è il p-value (Pro b> Chi2), meno probabile che i termini di errore siano omogenei.

L’opzione studentize è importante quando si tratta di residui con distribuzione heavy tailed (gli eventi rari hanno una probabilità relativamente alta di verificarsi). L’opzione varformula consente l’introduzione del test White.

BREUSCH-PAGAN test:

# The studentized Breusch-Pagan test
t <- 1:length(y_res)
y_res_stud_BP <- lmtest::bptest(formula=y_res~t, varformula=NULL, studentize=TRUE, data=NULL)
show(y_res_stud_BP)

    studentized Breusch-Pagan test

data:  y_res ~ t
BP = 2.9096, df = 1, p-value = 0.08806

Un p-value elevato, come questo (superiore a 0.05), indica che non ci sono prove sufficienti per respingere l’ipotesi nulla di omoschedasticità. Questo significa che non è possibile affermare con certezza che la varianza dei termini di errore non sia costante e che, quindi, sia presente eteroschedasticità.

WHITE test:

# The studentized White test
t <- 1:length(y_res)
y_res_stud_W <- lmtest::bptest(formula=y_res~t, varformula=y_res~t+I(t^2), studentize=TRUE, data=NULL)
show(y_res_stud_W)

    studentized Breusch-Pagan test

data:  y_res ~ t
BP = 5.5062, df = 2, p-value = 0.06373

Unp-value elevato come questo (superiore a 0.05), indica che non ci sono prove sufficienti per respingere l’ipotesi nulla di omoschedasticità. Questo significa che non è possibile affermare con certezza che la varianza dei termini di errore non sia costante e che, quindi, sia presente eteroschedasticità.

Dai test appena svolti si può confermare che i residui sono generati da un rumore omoschedastico, come ci si era già accorti in precedenza analizzando visivamente il grafico “Residuals vs Fitted”.

Plot dell’autocorrelogramma

# Plot of the autocorrelogram.
y <- Camry_lm$residuals
T <- length(y)
# maxlag <- ceiling(10*log10(T))    # Default
# maxlag <- ceiling(sqrt(T)+45)     # Box-Jenkins
# maxlag <- ceiling(min(10, T/4))   # Hyndman (for data without seasonality)
maxlag <- ceiling(min(2*12, T/5))   # Hyndman (for data with seasonality)
# https://robjhyndman.com/hyndsight/ljung-box-test/
Aut_Fun_y <- acf(y, lag.max=maxlag, type="correlation", plot=FALSE)
ci_90 <- qnorm((1+0.90)/2)/sqrt(T)
ci_95 <- qnorm((1+0.95)/2)/sqrt(T)
ci_99 <- qnorm((1+0.99)/2)/sqrt(T)
Plot_Aut_Fun_y <- data.frame(lag=Aut_Fun_y$lag, acf=Aut_Fun_y$acf)
First_Date <- paste(Data_df$Month[1],Data_df$Year[1])
Last_Date <- paste(Data_df$Month[T],Data_df$Year[T])
title_content <- bquote(atop("Autocorrelogram of the Residuals of the Linear Model for Monthly Toyota Camry sales in the U.S. from ", .(First_Date), " to ", .(Last_Date)))
subtitle_content <- bquote(paste("Path length ", .(T), " sample points. Lags ", .(maxlag)))
caption_content <- "Author: Matteo Chiacchia"
x_name <- bquote("lags")
x_breaks_num <- maxlag
x_binwidth <- 1
x_breaks <- Aut_Fun_y$lag
x_labs <- format(x_breaks, scientific=FALSE)
ggplot(Plot_Aut_Fun_y, aes(x=lag, y=acf))+
  geom_segment(aes(x=lag, y=rep(0,length(lag)), xend=lag, yend=acf), linewidth=1, col="black") +
  # geom_col(mapping=NULL, data=NULL, position="dodge", width=0.1, col="black", inherit.aes=TRUE)+
  geom_hline(aes(yintercept=-ci_90, color="CI_90"), show.legend=TRUE, lty=3) +
  geom_hline(aes(yintercept=ci_90, color="CI_90"), lty=3) +
  geom_hline(aes(yintercept=ci_95, color="CI_95"), show.legend=TRUE, lty=4)+
  geom_hline(aes(yintercept=-ci_95, color="CI_95"), lty=4) +
  geom_hline(aes(yintercept=-ci_99, color="CI_99"), show.legend=TRUE, lty=4) +
  geom_hline(aes(yintercept=ci_99, color="CI_99"), lty=4) +
  scale_x_continuous(name="lag", breaks=x_breaks, label=x_labs) +
  scale_y_continuous(name="acf value", breaks=waiver(), labels=NULL,
                     sec.axis=sec_axis(~., breaks=waiver(), labels=waiver())) +
  scale_color_manual(name="Conf. Inter.", labels=c("90%","95%","99%"),
                     values=c(CI_90="green", CI_95="blue", CI_99="red")) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  theme(plot.title=element_text(hjust=0.5), 
        plot.subtitle=element_text(hjust= 0.5),
        plot.caption=element_text(hjust=1.0),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")

Si nota dal grafico una forte autocorrelazione. Per completezza si esegue il Ljiung-box test.

y <- y_res
maxlag <- ceiling(min(2*12, T/5))     # Hyndman https://robjhyndman.com/hyndsight/ljung-box-test/
Box.test(y, lag = maxlag, type = "Ljung-Box", fitdf = 0)

    Box-Ljung test

data:  y
X-squared = 153.92, df = 24, p-value < 2.2e-16

Il p-value inferiore a \(2.2 * 10^{-16}\) indica che c’è una forte evidenza che i residui non sono indipendenti e che esiste una forma di autocorrelazione seriale nei dati. Possiamo quindi affermare, in linea con l’evidenza visiva dell’autocorrelogramma e con il risultato del Ljung-Box test, che i residui del modello lineare non sono generati da un rumore con distribuzione indipendente e identicamente distribuita.

Grazie ai risultati appena ottenuti, possiamo rigettare il modello lineare come modello di predizione da utilizzare.

STL Model

Il primo passo è quello di isolare il Train Set per poterlo utilizzare nelll’analisi. Il modello ottenuto verrà poi utilizzato nella predizione dei valori relativi al Test Set per poi procedere con l’utilizzo di varie statistiche di accuratezza per valutare la predizione.

#trainset creation
Camry_TrnS_df <- df[1:TrnS_length,]
Camry_TstS_df <- df[TrnS_length+1: TrnS_length+TstS_length, ]
head(Camry_TrnS_df)
tail(Camry_TrnS_df)
Box-Cox trasformations

Le trasformazioni di Box-Cox a cui viene sottoposto il Train Set sono:

  • Trasformazione di Box-Cox col metodo Guerrero
  • Trasformazione di Box-Cox col metodo della massima verosimiglianza
  • Traformazione logaritmica
  • Trasformazione quadratica inversa
y <- Camry_TrnS_df$sales
y_BCT_Guerr_lambda <- forecast::BoxCox.lambda(y, method="guerrero")
tilde_y_BCT_Guerr_lambda <- forecast::BoxCox(y, lambda=y_BCT_Guerr_lambda)
y_BCT_loglik_lambda <- forecast::BoxCox.lambda(y, method="loglik")
tilde_y_BCT_loglik_lambda <- forecast::BoxCox(y, lambda=y_BCT_loglik_lambda)

Una volta calcolate le trasformate, vengono aggiunti i valori al dataframe.

Camry_TrnS_df <- add_column(Camry_TrnS_df, y_BCT_Guerr=tilde_y_BCT_Guerr_lambda, y_BCT_loglik=tilde_y_BCT_loglik_lambda, 
                          y_BCT_log=log(y), y_BCT_sqrt=sqrt(y), .after="sales")

Consideriamo i grafici (scatter e line plot) delle trasformate.

# We consider the scatter and line plots of the transformations
# The scatter plots
Data_df <- Camry_TrnS_df
length <- nrow(Data_df)
First_Date <- paste(Data_df$Month[1],Data_df$Year[1])
Last_Date <- paste(Data_df$Month[length],Data_df$Year[length])
title_content <- bquote(atop("Scatter Plot of Four Box-Cox Transformations of Toyota Camry sales in the U.S. from ", .(First_Date), " to ", .(Last_Date)))
caption_content <- "Author: Matteo Chiacchia"
subtitle_content <- bquote(paste("BCT Guerrero Method - Data set size ", .(length),~~"sample points"))
x_name <- bquote("")
# primeFactors(length)
# primeFactors(length-1)
x_breaks_num <- 39
x_breaks_min <- Data_df$t[1]
x_breaks_max <- Data_df$t[length]
x_binwidth <- floor((x_breaks_max-x_breaks_min)/x_breaks_num)
x_breaks <- seq(from=x_breaks_min, to=x_breaks_max, by=x_binwidth)
if((x_breaks_max-max(x_breaks))>x_binwidth/2){x_breaks <- c(x_breaks,x_breaks_max)}
# x_labs <- format(x_breaks, scientific=FALSE)
x_labs <- Data_df$Data[x_breaks]
J <- 0
x_lims <- c(x_breaks_min-J*x_binwidth, x_breaks_max+J*x_binwidth)
col_1 <- bquote("BTC Guerrero method")
col_2 <- bquote("BTC loglik. method")
col_3 <- bquote("Log transform.")
col_4 <- bquote("Sqrt transform.")
col_5 <- bquote("Regression line")
col_6 <- bquote("loess")
leg_labs <- c(col_1, col_2, col_3, col_4, col_5, col_6)
leg_cols <- c("col_1"="red", "col_2"="green", "col_3"="blue", "col_4"="magenta", "col_5"="black", "col_6"="brown")
leg_breaks <- c("col_1", "col_2", "col_3", "col_4", "col_5", "col_6")
# Box-Cox transformation-Guerrero method
subtitle_content <- bquote(paste("BCT Guerrero Method - Data set size ", .(length),~~"sample points"))
y_name <- bquote("Mon. Deaths per 10,000 (BCT-Guerr. Tr.)")
y_breaks_num <- 2
y_max <- max(Data_df$y_BCT_Guerr)
y_min <- min(Data_df$y_BCT_Guerr)
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- ceiling(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0.5
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
y_BCT_Guerr_sp <- ggplot(Data_df, aes(x=t))+
  geom_point(alpha=1, size=1.5, shape=19, aes(y=y_BCT_Guerr, color="col_1"), show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_Guerr, color="col_5"),
              method="lm" , formula=y ~ x, se=FALSE, fullrange=FALSE, show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=1.0, linetype="dashed", aes(x=t, y=y_BCT_Guerr, color="col_6"), 
              method="loess", formula=y ~ x, se=FALSE, show.legend=FALSE) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=NULL, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  #  ggtitle(title_content) +
  labs(subtitle=subtitle_content) +
  scale_color_manual(name="Legend", labels=leg_labs, values=leg_cols) +
  theme(plot.title=element_text(hjust=0.5, size=18), plot.subtitle=element_text(hjust=0),
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(y_BCT_Guerr_sp)
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing missing values (`geom_point()`).

# Box-Cox transformation-log-likelihood method 
subtitle_content <- bquote(paste("BCT Log-likelihood Method - Data set size ", .(length),~~"sample points"))
y_name <- bquote("Mon. Deaths per 10,000 (BCT-Loglik. Tr.)")
y_breaks_num <- 2
y_max <- max(Data_df$y_BCT_loglik)
y_min <- min(Data_df$y_BCT_loglik)
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- ceiling(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0.5
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
y_BCT_loglik_sp <- ggplot(Data_df, aes(x=t))+
  geom_point(alpha=1, size=1.5, shape=19, aes(y=y_BCT_loglik, color="col_2"), show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_loglik, color="col_5"),
              method="lm" , formula=y ~ x, se=FALSE, fullrange=FALSE, show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=1.0, linetype="dashed", aes(x=t, y=y_BCT_loglik, color="col_6"), 
              method="loess", formula=y ~ x, se=FALSE, show.legend=FALSE) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=NULL, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  labs(subtitle=subtitle_content) +
  scale_color_manual(name="Legend", labels=leg_labs, values=leg_cols) +
  theme(plot.title=element_text(hjust=0.5, size=18), plot.subtitle=element_text(hjust=0), 
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(y_BCT_loglik_sp)
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing missing values (`geom_point()`).

# Logarithm transformation
subtitle_content <- bquote(paste("Logarithm Transformation - Data set size ", .(length),~~"sample points"))
y_name <- bquote("Mon. Deaths per 10,000 (Log. Tr.)")
y_breaks_num <- 2
y_max <- max(Data_df$y_BCT_log)
y_min <- min(Data_df$y_BCT_log)
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- ceiling(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0.5
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
y_BCT_log_sp <- ggplot(Data_df, aes(x=t)) +
  geom_point(alpha=1, size=1.5, shape=19, aes(y=y_BCT_log, color="col_3"), show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_log, color="col_5"),
              method="lm" , formula=y ~ x, se=FALSE, fullrange=FALSE, show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=1.0, linetype="dashed", aes(x=t, y=y_BCT_log, color="col_6"), 
              method="loess", formula=y ~ x, se=FALSE, show.legend=FALSE) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=x_labs, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  labs(subtitle=subtitle_content) +
  scale_color_manual(name="Legend", labels=leg_labs, values=leg_cols) +
  theme(plot.title=element_text(hjust=0.5, size=18), plot.subtitle=element_text(hjust=0), 
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(y_BCT_log_sp)

# Square Root transformation
subtitle_content <- bquote(paste("Square Root Transformation - Data set size ", .(length),~~"sample points"))
y_name <- bquote("Mon. Deaths per 10,000 (Sqrt Tr.)")
y_breaks_num <- 2
y_max <- max(Data_df$y_BCT_sqrt)
y_min <- min(Data_df$y_BCT_sqrt)
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- floor(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0.0
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
y_BCT_sqrt_sp <- ggplot(Data_df, aes(x=t))+
  geom_point(alpha=1, size=1.5, shape=19, aes(y=y_BCT_sqrt, color="col_4")) +
  geom_smooth(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_sqrt, color="col_5"),
              method="lm" , formula=y ~ x, se=FALSE, fullrange=FALSE, show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=1.0, linetype="dashed", aes(x=t, y=y_BCT_sqrt, color="col_6"), 
              method="loess", formula=y ~ x, se=FALSE, show.legend=FALSE) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=x_labs, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  labs(subtitle=subtitle_content) +
  scale_color_manual(name="Legend", labels=leg_labs, values=leg_cols) +
  guides(colour=guide_legend(nrow=1)) +
  theme(plot.title=element_text(hjust=0.5, size=18), plot.subtitle=element_text(hjust=0), 
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(y_BCT_sqrt_sp)


g_legend <- function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  return(legend)}
mylegend <- g_legend(y_BCT_sqrt_sp)
#
grid.arrange(arrangeGrob(y_BCT_Guerr_sp+theme(legend.position="none"),
                         y_BCT_loglik_sp+theme(legend.position="none"),
                         y_BCT_log_sp+theme(legend.position="none"),
                         y_BCT_sqrt_sp+theme(legend.position="none"),
                         nrow=2),
             top=textGrob(title_content, gp=gpar(fontface=1, cex=1.1)),
             bottom=textGrob(caption_content, gp=gpar(fontface=3, fontsize=9), hjust=1, x=1),
             mylegend, nrow=2, heights=c(10, 1))
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing missing values (`geom_point()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing missing values (`geom_point()`).

title_content <- bquote(atop("Line Plot of Four Box-Cox Transformations of Toyota Camry sales in the U.S. from ", .(First_Date), " to ", .(Last_Date)))

# Box-Cox transformation-Guerrero method
subtitle_content <- bquote(paste("BCT Guerrero Method - Data set size ", .(length),~~"sample points"))
y_name <- bquote("Mon. Deaths per 10,000 (BCT-Guerr. Tr.)")
y_breaks_num <- 2
y_max <- max(Data_df$y_BCT_Guerr)
y_min <- min(Data_df$y_BCT_Guerr)
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- ceiling(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0.5
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
y_BCT_Guerr_lp <- ggplot(Data_df, aes(x=t))+
  geom_line(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_Guerr, color="col_1", group=1), show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_Guerr, color="col_5"),
              method="lm" , formula=y ~ x, se=FALSE, fullrange=FALSE, show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=1.0, linetype="dashed", aes(x=t, y=y_BCT_Guerr, color="col_6"), 
              method="loess", formula=y ~ x, se=FALSE, show.legend=FALSE) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=NULL, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  #  ggtitle(title_content) +
  labs(subtitle=subtitle_content) +
  scale_color_manual(name="Legend", labels=leg_labs, values=leg_cols) +
  theme(plot.title=element_text(hjust=0.5), plot.subtitle=element_text(hjust=0),
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(y_BCT_Guerr_lp)
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).

# Box-Cox transformation-log-likelihood method 
subtitle_content <- bquote(paste("BCT Log-likelihood Method - Data set size ", .(length),~~"sample points"))
y_name <- bquote("Mon. Deaths per 10,000 (BCT-Loglik. Tr.)")
y_breaks_num <- 2
y_max <- max(Data_df$y_BCT_loglik)
y_min <- min(Data_df$y_BCT_loglik)
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- ceiling(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0.5
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
y_BCT_loglik_lp <- ggplot(Data_df, aes(x=t))+
  geom_line(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_loglik, color="col_2", group=1), show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_loglik, color="col_5"),
              method="lm" , formula=y ~ x, se=FALSE, fullrange=FALSE, show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=1.0, linetype="dashed", aes(x=t, y=y_BCT_loglik, color="col_6"), 
              method="loess", formula=y ~ x, se=FALSE, show.legend=FALSE) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=NULL, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  labs(subtitle=subtitle_content) +
  scale_color_manual(name="Legend", labels=leg_labs, values=leg_cols) +
  theme(plot.title=element_text(hjust=0.5), plot.subtitle=element_text(hjust=0), 
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(y_BCT_loglik_lp)
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).

# Logarithm transformation
subtitle_content <- bquote(paste("Logarithm Transformation - Data set size ", .(length),~~"sample points"))
y_name <- bquote("Mon. Deaths per 10,000 (Log. Tr.)")
y_breaks_num <- 2
y_max <- max(Data_df$y_BCT_log)
y_min <- min(Data_df$y_BCT_log)
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- ceiling(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0.5
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
y_BCT_log_lp <- ggplot(Data_df, aes(x=t)) +
  geom_line(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_log, color="col_3", group=1), show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_log, color="col_5"),
              method="lm" , formula=y ~ x, se=FALSE, fullrange=FALSE, show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=1.0, linetype="dashed", aes(x=t, y=y_BCT_log, color="col_6"), 
              method="loess", formula=y ~ x, se=FALSE, show.legend=FALSE) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=x_labs, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  labs(subtitle=subtitle_content) +
  scale_color_manual(name="Legend", labels=leg_labs, values=leg_cols) +
  theme(plot.title=element_text(hjust=0.5), plot.subtitle=element_text(hjust=0), 
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(y_BCT_log_lp)

# Square Root transformation
subtitle_content <- bquote(paste("Square Root Transformation - Data set size ", .(length),~~"sample points"))
y_name <- bquote("Mon. Deaths per 10,000 (Sqrt Tr.)")
y_breaks_num <- 2
y_max <- max(Data_df$y_BCT_sqrt)
y_min <- min(Data_df$y_BCT_sqrt)
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- floor(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0.0
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
y_BCT_sqrt_lp <- ggplot(Data_df, aes(x=t))+
  geom_line(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_sqrt, color="col_4", group=1), show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=0.8, linetype="solid", aes(x=t, y=y_BCT_sqrt, color="col_5"),
              method="lm" , formula=y ~ x, se=FALSE, fullrange=FALSE, show.legend=FALSE) +
  geom_smooth(alpha=1, linewidth=1.0, linetype="dashed", aes(x=t, y=y_BCT_sqrt, color="col_6"), 
              method="loess", formula=y ~ x, se=FALSE, show.legend=FALSE) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=x_labs, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  labs(subtitle=subtitle_content) +
  scale_color_manual(name="Legend", labels=leg_labs, values=leg_cols) +
  guides(colour=guide_legend(nrow=1)) +
  theme(plot.title=element_text(hjust=0.5), plot.subtitle=element_text(hjust=0), 
        axis.text.x=element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")
plot(y_BCT_sqrt_lp)

grid.arrange(arrangeGrob(y_BCT_Guerr_lp+theme(legend.position="none"),
                         y_BCT_loglik_lp+theme(legend.position="none"),
                         y_BCT_log_lp+theme(legend.position="none"),
                         y_BCT_sqrt_lp+theme(legend.position="none"),
                         nrow=2),
             top=textGrob(title_content, gp=gpar(fontface=1, cex=1.1)),
             bottom=textGrob(caption_content, gp=gpar(fontface=3, fontsize=9), hjust=1, x=1),
             mylegend, nrow=2, heights=c(10, 1))
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).
Avvertimento: Removed 1 rows containing non-finite values (`stat_smooth()`).

Per il resto dell’analisi si utilizzerà la trasformata che restituisce i migliori valori in termini di omoschedasticità.

Guerrero Method:

y_BCT_Guerr_stud_BP <- lmtest::bptest(formula=y_BCT_Guerr~t, varformula=NULL, studentize=FALSE, data=Data_df)
show(y_BCT_Guerr_stud_BP)

    Breusch-Pagan test

data:  y_BCT_Guerr ~ t
BP = 1.9107, df = 1, p-value = 0.1669
# The unstudentized White test
y_BCT_Guerr_stud_W <- lmtest::bptest(formula=y_BCT_Guerr~t, varformula=y_BCT_Guerr~t+I(t^2), 
                                     studentize=FALSE, data=Data_df)
show(y_BCT_Guerr_stud_W)

    Breusch-Pagan test

data:  y_BCT_Guerr ~ t
BP = 8.2187, df = 2, p-value = 0.01642

Log-Lik Method:

# The unstudentized Breusch-Pagan test
y_BCT_loglik_stud_BP <- lmtest::bptest(formula=y_BCT_loglik~t, varformula=NULL, studentize=FALSE, 
                                       data=Data_df)
show(y_BCT_loglik_stud_BP)

    Breusch-Pagan test

data:  y_BCT_loglik ~ t
BP = 3.7343, df = 1, p-value = 0.05331
# The unstudentized White test
y_BCT_loglik_stud_W <- lmtest::bptest(formula=y_BCT_loglik~t, varformula=y_BCT_loglik~t+I(t^2), 
                                      studentize=FALSE, data=Data_df)
show(y_BCT_loglik_stud_W)

    Breusch-Pagan test

data:  y_BCT_loglik ~ t
BP = 8.0165, df = 2, p-value = 0.01816

Log Method:

# The studentized Breusch-Pagan test
y_BCT_log_stud_BP <- lmtest::bptest(formula=y_BCT_log~t, varformula=NULL, studentize=TRUE, data=Data_df)
show(y_BCT_log_stud_BP)

    studentized Breusch-Pagan test

data:  y_BCT_log ~ t
BP = 0.037614, df = 1, p-value = 0.8462
#
# The studentized White test
y_BCT_log_stud_W <- lmtest::bptest(formula=y_BCT_log~t, varformula=y_BCT_log~t+I(t^2), studentize=TRUE, data=Data_df)
show(y_BCT_log_stud_W)

    studentized Breusch-Pagan test

data:  y_BCT_log ~ t
BP = 3.9705, df = 2, p-value = 0.1373

Square Method:

# The studentized Breusch-Pagan test
y_BCT_sqrt_stud_BP <- lmtest::bptest(formula=y_BCT_sqrt~t, varformula=NULL, studentize=TRUE, data=Data_df)
show(y_BCT_sqrt_stud_BP)

    studentized Breusch-Pagan test

data:  y_BCT_sqrt ~ t
BP = 1.0111, df = 1, p-value = 0.3146
# We have to reject the null of homoscedasticity at the significance level $\alpha=0.1$ or $\alpha=10\%$.

# The studentized White test
y_BCT_sqrt_stud_W <- lmtest::bptest(formula=y_BCT_sqrt~t, varformula=y_BCT_sqrt~t+I(t^2), studentize=TRUE, 
                                    data=Data_df)
show(y_BCT_sqrt_stud_W)

    studentized Breusch-Pagan test

data:  y_BCT_sqrt ~ t
BP = 4.9177, df = 2, p-value = 0.08553

I risultati migliori vengono restituiti dalla trasformata logaritmica (Breusch-Pagan test p-value = 0.8462, White test p-value = 0.1373)

STL DECOMPOSITION

La decomposizione STL è stata applicata alla serie temporale trasformata al fine di suddividerla in tre componenti: trend, stagionalità e remainder. Due parametri della decomposizione STL devono essere definiti: la finestra per il trend e la finestra per la stagionalità. Verranno testati i seguenti set di parametri e verrà selezionato quello che fornisce il remainder con il minor valore di autocorrelazione:

  • Finestra trend = NULL e finestra stagionalità = NULL (il che significa che le due finestre saranno selezionate automaticamente dalla funzione R)
  • Finestra trend = 19 e finestra stagionalità = 11
  • Finestra trend = 23 e finestra stagionalità = 11
  • Finestra trend = 27 e finestra stagionalità = 11
  • Finestra trend = 29 e finestra stagionalità = 11
  • Finestra trend = 31 e finestra stagionalità = 11

Questa scelta è stata valutata sulla base dell’ispezione visiva delle tre componenti della serie temporale, dell’ispezione visiva degli autocorrelagrammi totale e parziale e dei risultati dei test di valutazione delle ipotesi di omoschedasticità, mancanza di correlazione e stazionarietà del remainder STL. La finestra della stagionalità è stata impostata su 11 perché abbiamo una stagionalità annuale nella serie temporale, mentre la finestra del trend viene cambiata. Più alta è la finestra del trend, più tempo (ovvero mesi) è necessario per i cambiamenti del trend.

Camry_TrnS_ts <- ts(Camry_TrnS_df$y_BCT_log, start=c(2009, 01), end=c(2021, 11), frequency = 12)
y_log <- Camry_TrnS_df$y_BCT_log
Camry_TrnS_ts_tsibble <- as_tsibble(Camry_TrnS_ts, key = NULL, index = date)
Data_tsibble <- Camry_TrnS_ts_tsibble

Plot della decomposizione STL

Data_tsibble %>%
  model(STL(y_log ~ trend(window=29)+season(period="1 year", window=11), robust=TRUE)) %>%
  components() %>% 
  autoplot()+labs(x="Data")

Creazione delle componenti (trend, seasonality, remainder) derivate dalla decomposizione.

y_BCT_log_STL_def_win_dcmp_ts <- Data_tsibble %>%
  model(STL(y_log ~ trend(window=29)+season(period="1 year", window=11), robust=TRUE)) %>%
  components()
y <- y_BCT_log_STL_def_win_dcmp_ts$remainder
T <- length(y)
maxlag <- ceiling(min(10, T/4))     # Hyndman (for data without seasonality)
Box.test(y, lag = maxlag,type = "Ljung-Box", fitdf = 0)

    Box-Ljung test

data:  y
X-squared = 53.344, df = 10, p-value = 6.428e-08
Data_df <- Camry_TrnS_df
y <- y_BCT_log_STL_def_win_dcmp_ts$remainder
T <- length(y)
# maxlag <- ceiling(10*log10(T))    # Default
# maxlag <- ceiling(sqrt(n)+45)     # Box-Jenkins
maxlag <- ceiling(min(10, T/4))     # Hyndman (for data without seasonality)
# maxlag <- ceiling(min(2*12, T/5)) # Hyndman (for data with seasonality)
# https://robjhyndman.com/hyndsight/ljung-box-test/
Aut_Fun_y <- acf(y, lag.max=maxlag, type="correlation", plot=FALSE)
ci_90 <- qnorm((1+0.90)/2)/sqrt(T)
ci_95 <- qnorm((1+0.95)/2)/sqrt(T)
ci_99 <- qnorm((1+0.99)/2)/sqrt(T)
Plot_Aut_Fun_y <- data.frame(lag=Aut_Fun_y$lag, acf=Aut_Fun_y$acf)
First_Date <- paste(Data_df$Month[1],Data_df$Year[1])
Last_Date <- paste(Data_df$Month[T],Data_df$Year[T])
title_content <- bquote(atop("Autocorrelogram of the Remainders in the STL Decomp. for the Log. Box-Cox Transf. of the Training Set from ", .(First_Date), " to ", .(Last_Date)))
subtitle_content <- bquote(paste("Path length ", .(T), " sample points. Lags ", .(maxlag)))
caption_content <- "Author: Matteo Chiacchia"
x_name <- bquote("lags")
x_breaks_num <- maxlag
x_binwidth <- 1
x_breaks <- Aut_Fun_y$lag
#class(x_breaks)
x_labs <- format(x_breaks, scientific=FALSE)
ggplot(Plot_Aut_Fun_y, aes(x=lag, y=acf))+
  geom_segment(aes(x=lag, y=rep(0,length(lag)), xend=lag, yend=acf), linewidth=1, col="black") +
  # geom_col(mapping=NULL, data=NULL, position="dodge", width=0.1, col="black", inherit.aes=TRUE)+
  geom_hline(aes(yintercept=-ci_90, color="CI_90"), show.legend=TRUE, lty=3) +
  geom_hline(aes(yintercept=ci_90, color="CI_90"), lty=3) +
  geom_hline(aes(yintercept=ci_95, color="CI_95"), show.legend=TRUE, lty=4)+
  geom_hline(aes(yintercept=-ci_95, color="CI_95"), lty=4) +
  geom_hline(aes(yintercept=-ci_99, color="CI_99"), show.legend=TRUE, lty=4) +
  geom_hline(aes(yintercept=ci_99, color="CI_99"), lty=4) +
  scale_x_continuous(name="lag", breaks=x_breaks, label=x_labs) +
  scale_y_continuous(name="acf value", breaks=waiver(), labels=NULL,
                     sec.axis=sec_axis(~., breaks=waiver(), labels=waiver())) +
  scale_color_manual(name="Conf. Inter.", labels=c("90%","95%","99%"),
                     values=c(CI_90="green", CI_95="blue", CI_99="red")) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  theme(plot.title=element_text(hjust=0.5, size=9), 
        plot.subtitle=element_text(hjust= 0.5, size=8.5),
        plot.caption=element_text(hjust=1.0),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")

Data_df <- Camry_TrnS_df
y <- y_BCT_log_STL_def_win_dcmp_ts$remainder
T <- length(y)
# maxlag <- ceiling(10*log10(T))    # Default
# maxlag <- ceiling(sqrt(n)+45)     # Box-Jenkins
maxlag <- ceiling(min(10, T/4))     # Hyndman (for data without seasonality)
# maxlag <- ceiling(min(2*12, T/5)) # Hyndman (for data with seasonality)
# https://robjhyndman.com/hyndsight/ljung-box-test/
Aut_Fun_y <- pacf(y, lag.max=maxlag, type="correlation", plot=FALSE)
ci_90 <- qnorm((1+0.90)/2)/sqrt(T)
ci_95 <- qnorm((1+0.95)/2)/sqrt(T)
ci_99 <- qnorm((1+0.99)/2)/sqrt(T)
Plot_Aut_Fun_y <- data.frame(lag=Aut_Fun_y$lag, acf=Aut_Fun_y$acf)
First_Date <- paste(Data_df$Month[1],Data_df$Year[1])
Last_Date <- paste(Data_df$Month[T],Data_df$Year[T])
title_content <- bquote(atop("Partial autocorrelogram of the Remainders in the STL Decomp. for the Log. Box-Cox Transf. of the Training Set from ", .(First_Date), " to ", .(Last_Date)))
subtitle_content <- bquote(paste("Path length ", .(T), " sample points. Lags ", .(maxlag)))
caption_content <- "Author: Matteo Chiacchia"
x_name <- bquote("lags")
x_breaks_num <- maxlag
x_binwidth <- 1
x_breaks <- Aut_Fun_y$lag
#class(x_breaks)
x_labs <- format(x_breaks, scientific=FALSE)
ggplot(Plot_Aut_Fun_y, aes(x=lag, y=acf))+
  geom_segment(aes(x=lag, y=rep(0,length(lag)), xend=lag, yend=acf), linewidth=1, col="black") +
  # geom_col(mapping=NULL, data=NULL, position="dodge", width=0.1, col="black", inherit.aes=TRUE)+
  geom_hline(aes(yintercept=-ci_90, color="CI_90"), show.legend=TRUE, lty=3) +
  geom_hline(aes(yintercept=ci_90, color="CI_90"), lty=3) +
  geom_hline(aes(yintercept=ci_95, color="CI_95"), show.legend=TRUE, lty=4)+
  geom_hline(aes(yintercept=-ci_95, color="CI_95"), lty=4) +
  geom_hline(aes(yintercept=-ci_99, color="CI_99"), show.legend=TRUE, lty=4) +
  geom_hline(aes(yintercept=ci_99, color="CI_99"), lty=4) +
  scale_x_continuous(name="lag", breaks=x_breaks, label=x_labs) +
  scale_y_continuous(name="acf value", breaks=waiver(), labels=NULL,
                     sec.axis=sec_axis(~., breaks=waiver(), labels=waiver())) +
  scale_color_manual(name="Conf. Inter.", labels=c("90%","95%","99%"),
                     values=c(CI_90="green", CI_95="blue", CI_99="red")) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  theme(plot.title=element_text(hjust=0.5, size=9), 
        plot.subtitle=element_text(hjust= 0.5, size=8.5),
        plot.caption=element_text(hjust=1.0),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")

Tutti i set di parametri STL testati forniscono un remainder che può essere considerato omoschedastico e stazionario. Per quanto riguarda la correlazione, invece, tutti i set forniscono un remainder autocorrelato, di conseguenza si andrà a utlizzare il modello ARMA per poter descrivere nel modo migliore questo tipo di autocorrelazione.

L’analisi verrà continuata utilizzando la decomposizione di tempo:

  • trend window = 29
  • season window = 11

L’idea, infatti, è stata quella di catturare al meglio le variazione a lungo termine piuttosto che quelle a breve termine.

y_BCT_log_STL_remainder <- y_BCT_log_STL_def_win_dcmp_ts$remainder
ARMA model

I modelli ARMA (Autoregressive Moving Average) sono una classe di modelli statistici utilizzati per analizzare serie temporali. Questi modelli combinano due componenti principali: l’autoregressione (AR) e la media mobile (MA).

Il componente AR utilizza i valori precedenti della serie temporale per prevedere i valori futuri. Ciò significa che il valore corrente dipende dalle osservazioni precedenti. Il grado di dipendenza dai valori precedenti dipende dall’ordine del modello AR.

Il componente MA, d’altra parte, utilizza l’errore residuo della previsione dell’autoregressione per prevedere i valori futuri. L’ordine del modello MA determina il numero di errori residui utilizzati nella previsione.

L’idea è quella di poter rappresentare il remainder tramite un modello ARMA. Sono stati adottati due diversi approcci in R per identificare il miglior modello ARMA. Uno si basa su una funzione integrata di R (“auto.arima”), mentre l’altro si basa sulla selezione manuale del miglior modello ARMA tra quelli testati. Entrambi i metodi selezionano il miglior modello tramite il valore più basso di AIC ottenuto. In statistica, l’AIC (Akaike’s Information Criterion) è una misura di bontà di adattamento di un modello statistico ai dati osservati. Esso fornisce una stima relativa della qualità di un modello rispetto ad altri modelli alternativi.

L’idea alla base dell’AIC è di penalizzare i modelli che includono troppi parametri rispetto al numero di osservazioni, poiché questi modelli rischiano di sovra-adattarsi ai dati di apprendimento e di perdere la capacità di generalizzare ad altri dati (overfitting). Pertanto, tra i modelli che forniscono un buon adattamento ai dati, l’AIC suggerisce di scegliere quello con il valore minore.

y <- y_BCT_log_STL_remainder
ARIMA_y <- list()
# Setting a counter
cn <- 1
# Looping over include.mean
for(flag in 0:1){
  # Looping over p
  for(p in 0:6){
    # Looping over q
    for(q in 0:6){
    #ERROR and WARNINGS HANDLING
    tryCatch({
      ARIMA_y[[cn]] <- arima(y, order=c(p,0,q), include.mean=flag, method="CSS-ML")
      cn <- cn+1
    }, error = function(e){
    }, warning = function (w){
    }
    )
  }
 } 
}

# Selecting the best model by the Akaike Information Criterium
ARIMA_y_AIC <- sapply(ARIMA_y, function(x) x$aic)
#show(ARIMA_y_AIC)
ARIMA_y_min_AIC <- ARIMA_y[[which(ARIMA_y_AIC==min(ARIMA_y_AIC))]]
show(ARIMA_y_min_AIC)

Call:
arima(x = y, order = c(p, 0, q), include.mean = flag, method = "CSS-ML")

Coefficients:
          ar1     ar2      ar3      ar4     ma1     ma2     ma3     ma4     ma5    ma6
      -0.5654  0.3966  -0.3506  -0.8765  1.1162  0.1088  0.4301  1.2928  0.6065  0.068
s.e.   0.0612  0.0790   0.0559   0.0468  0.1177  0.1645  0.0698  0.0749  0.1529  0.103

sigma^2 estimated as 0.01842:  log likelihood = 84.59,  aic = -147.19

Tramite la selezione manuale otteniamo che il modello ARIMA (4,0,6) è quello che restituisce un valore di AIC minore.

Adesso si studia la correlazione dei residui del modello tramite il test di LB test.

T <- length(y)
maxlag <- ceiling(min(10, T/4)) 
ARIMA_y_min_AIC_LB <- Box.test(ARIMA_y[[which(ARIMA_y_AIC==min(ARIMA_y_AIC))]][["residuals"]], 
                                   lag=maxlag)
show(ARIMA_y_min_AIC_LB)

    Box-Pierce test

data:  ARIMA_y[[which(ARIMA_y_AIC == min(ARIMA_y_AIC))]][["residuals"]]
X-squared = 3.3302, df = 10, p-value = 0.9726

Il p-value resituito ci suggerisce che non è presente correlazione nei residui del modello ARIMA (4,0,6).

Viene utilizzata anche la funzione automatica per verificare quale è il miglior modello adatto a descrivere il remainder STL.

y <- y_BCT_log_STL_remainder
AUTOARIMA_y_AIC <- auto.arima(y, start.p=0, max.p=6, start.q=0, max.q=6, max.order=12, ic="aic", 
                              allowmean=TRUE, trace=TRUE, stepwise=FALSE, nmodels=94, 
                              approximation=FALSE)

 ARIMA(0,0,0) with zero mean     : -101.8462
 ARIMA(0,0,0) with non-zero mean : -101.3949
 ARIMA(0,0,1) with zero mean     : -132.0508
 ARIMA(0,0,1) with non-zero mean : -130.9961
 ARIMA(0,0,2) with zero mean     : -134.8218
 ARIMA(0,0,2) with non-zero mean : -133.5382
 ARIMA(0,0,3) with zero mean     : -132.8333
 ARIMA(0,0,3) with non-zero mean : -131.5426
 ARIMA(0,0,4) with zero mean     : -133.8778
 ARIMA(0,0,4) with non-zero mean : -132.4318
 ARIMA(0,0,5) with zero mean     : -131.8822
 ARIMA(0,0,5) with non-zero mean : -130.4318
 ARIMA(0,0,6) with zero mean     : -136.7157
 ARIMA(0,0,6) with non-zero mean : -135.6465
 ARIMA(1,0,0) with zero mean     : -136.8321
 ARIMA(1,0,0) with non-zero mean : -135.4431
 ARIMA(1,0,1) with zero mean     : -135.0673
 ARIMA(1,0,1) with non-zero mean : -133.7086
 ARIMA(1,0,2) with zero mean     : -133.0802
 ARIMA(1,0,2) with non-zero mean : -131.5624
 ARIMA(1,0,3) with zero mean     : -131.0842
 ARIMA(1,0,3) with non-zero mean : -129.6011
 ARIMA(1,0,4) with zero mean     : -131.8787
 ARIMA(1,0,4) with non-zero mean : -130.4318
 ARIMA(1,0,5) with zero mean     : -132.9204
 ARIMA(1,0,5) with non-zero mean : -132.3903
 ARIMA(1,0,6) with zero mean     : -135.9855
 ARIMA(1,0,6) with non-zero mean : -133.8687
 ARIMA(2,0,0) with zero mean     : -135.0782
 ARIMA(2,0,0) with non-zero mean : -133.7237
 ARIMA(2,0,1) with zero mean     : -134.8585
 ARIMA(2,0,1) with non-zero mean : -134.2514
 ARIMA(2,0,2) with zero mean     : -133.0356
 ARIMA(2,0,2) with non-zero mean : -132.3765
 ARIMA(2,0,3) with zero mean     : Inf
 ARIMA(2,0,3) with non-zero mean : Inf
 ARIMA(2,0,4) with zero mean     : -132.0836
 ARIMA(2,0,4) with non-zero mean : -130.6788
 ARIMA(2,0,5) with zero mean     : Inf
 ARIMA(2,0,5) with non-zero mean : Inf
 ARIMA(2,0,6) with zero mean     : -135.8401
 ARIMA(2,0,6) with non-zero mean : -135.2556
 ARIMA(3,0,0) with zero mean     : -133.0952
 ARIMA(3,0,0) with non-zero mean : -131.7494
 ARIMA(3,0,1) with zero mean     : Inf
 ARIMA(3,0,1) with non-zero mean : -129.7585
 ARIMA(3,0,2) with zero mean     : Inf
 ARIMA(3,0,2) with non-zero mean : Inf
 ARIMA(3,0,3) with zero mean     : Inf
 ARIMA(3,0,3) with non-zero mean : Inf
 ARIMA(3,0,4) with zero mean     : Inf
 ARIMA(3,0,4) with non-zero mean : Inf
 ARIMA(3,0,5) with zero mean     : Inf
 ARIMA(3,0,5) with non-zero mean : Inf
 ARIMA(3,0,6) with zero mean     : Inf
 ARIMA(3,0,6) with non-zero mean : Inf
 ARIMA(4,0,0) with zero mean     : -131.2601
 ARIMA(4,0,0) with non-zero mean : -129.9426
 ARIMA(4,0,1) with zero mean     : -129.1092
 ARIMA(4,0,1) with non-zero mean : -131.124
 ARIMA(4,0,2) with zero mean     : Inf
 ARIMA(4,0,2) with non-zero mean : Inf
 ARIMA(4,0,3) with zero mean     : Inf
 ARIMA(4,0,3) with non-zero mean : Inf
 ARIMA(4,0,4) with zero mean     : -136.6585
 ARIMA(4,0,4) with non-zero mean : -135.468
 ARIMA(4,0,5) with zero mean     : Inf
 ARIMA(4,0,5) with non-zero mean : Inf
 ARIMA(4,0,6) with zero mean     : Inf
 ARIMA(4,0,6) with non-zero mean : Inf
 ARIMA(5,0,0) with zero mean     : -134.1059
 ARIMA(5,0,0) with non-zero mean : -133.0173
 ARIMA(5,0,1) with zero mean     : -132.819
 ARIMA(5,0,1) with non-zero mean : -131.863
 ARIMA(5,0,2) with zero mean     : -131.8064
 ARIMA(5,0,2) with non-zero mean : -130.7178
 ARIMA(5,0,3) with zero mean     : Inf
 ARIMA(5,0,3) with non-zero mean : Inf
 ARIMA(5,0,4) with zero mean     : -129.4362
 ARIMA(5,0,4) with non-zero mean : Inf
 ARIMA(5,0,5) with zero mean     : Inf
 ARIMA(5,0,5) with non-zero mean : Inf
 ARIMA(5,0,6) with zero mean     : Inf
 ARIMA(5,0,6) with non-zero mean : Inf
 ARIMA(6,0,0) with zero mean     : -133.6632
 ARIMA(6,0,0) with non-zero mean : -132.7563
 ARIMA(6,0,1) with zero mean     : -136.8651
 ARIMA(6,0,1) with non-zero mean : -135.9422
 ARIMA(6,0,2) with zero mean     : Inf
 ARIMA(6,0,2) with non-zero mean : Inf
 ARIMA(6,0,3) with zero mean     : Inf
 ARIMA(6,0,3) with non-zero mean : Inf
 ARIMA(6,0,4) with zero mean     : Inf
 ARIMA(6,0,4) with non-zero mean : Inf
 ARIMA(6,0,5) with zero mean     : Inf
 ARIMA(6,0,5) with non-zero mean : Inf
 ARIMA(6,0,6) with zero mean     : Inf
 ARIMA(6,0,6) with non-zero mean : Inf



 Best model: ARIMA(6,0,1) with zero mean     

La funzione automatica resituisce come miglior modello il modello ARIMA (6,0,1). Dai valori restituiti, però, si nota come anche il modello ARIMA (1,0,0) restituisca un valore di AIC molto simile al precedente. Per decididere quale modello utilizzare si effettuano i vari test e si verifica, prendendo in considerazione anche la complessità del modello, quale restituisce i valori migliori.

ARIMA (4,0,6)

y <-y_BCT_log_STL_remainder
num_lags <- 0   # Setting the lag parameter for the test.
AUTOARIMA_y_AIC <- arima(y, order=c(4,0,6), include.mean=FALSE, method="CSS-ML")
arima_res=AUTOARIMA_y_AIC[['residuals']]
# The studentized BP test
ARIMA_y_AIC_BP <- lmtest::bptest(formula=arima_res~t, varformula=NULL, studentize=TRUE, data=Data_df)
show(ARIMA_y_AIC_BP)

    studentized Breusch-Pagan test

data:  arima_res ~ t
BP = 0.00062446, df = 1, p-value = 0.9801
# The studentized White test
ARIMA_y_AIC_W <- lmtest::bptest(formula=arima_res~t, varformula=arima_res~t+I(t^2), studentize=TRUE, data=Data_df)
show(ARIMA_y_AIC_W)

    studentized Breusch-Pagan test

data:  arima_res ~ t
BP = 2.8884, df = 2, p-value = 0.2359
# The ADF test
ARIMA_y_AIC_ADF <- ur.df(AUTOARIMA_y_AIC[['residuals']], type="none", lags=num_lags, selectlags="Fixed")    
summary(ARIMA_y_AIC_ADF)  

############################################### 
# Augmented Dickey-Fuller Test Unit Root Test # 
############################################### 

Test regression none 


Call:
lm(formula = z.diff ~ z.lag.1 - 1)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.89501 -0.05149  0.00014  0.05861  0.42289 

Coefficients:
        Estimate Std. Error t value Pr(>|t|)    
z.lag.1  -1.0016     0.0808  -12.39   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1365 on 153 degrees of freedom
Multiple R-squared:  0.501, Adjusted R-squared:  0.4978 
F-statistic: 153.6 on 1 and 153 DF,  p-value: < 2.2e-16


Value of test-statistic is: -12.3952 

Critical values for test statistics: 
      1pct  5pct 10pct
tau1 -2.58 -1.95 -1.62
# The KPSS test
ARIMA_y_AIC_KPSS <- ur.kpss(AUTOARIMA_y_AIC[['residuals']], type="mu", lags="nil", use.lag=NULL)    
summary(ARIMA_y_AIC_KPSS)    # Showing the result of the test

####################### 
# KPSS Unit Root Test # 
####################### 

Test is of type: mu with 0 lags. 

Value of test-statistic is: 0.1032 

Critical value for a significance level of: 
                10pct  5pct 2.5pct  1pct
critical values 0.347 0.463  0.574 0.739
# The LB test
ARIMA_y_AIC_LB <- Box.test(AUTOARIMA_y_AIC[["residuals"]], lag=maxlag)
show(ARIMA_y_AIC_LB)

    Box-Pierce test

data:  AUTOARIMA_y_AIC[["residuals"]]
X-squared = 3.3302, df = 10, p-value = 0.9726

ARIMA (6,0,1)

AUTOARIMA_y_AIC <- arima(y, order=c(6,0,1), include.mean=FALSE, method="CSS-ML")
arima_res=AUTOARIMA_y_AIC[['residuals']]
# The studentized BP test
ARIMA_y_AIC_BP <- lmtest::bptest(formula=arima_res~t, varformula=NULL, studentize=TRUE, data=Data_df)
show(ARIMA_y_AIC_BP)

    studentized Breusch-Pagan test

data:  arima_res ~ t
BP = 0.082394, df = 1, p-value = 0.7741
# The studentized White test
ARIMA_y_AIC_W <- lmtest::bptest(formula=arima_res~t, varformula=arima_res~t+I(t^2), studentize=TRUE, data=Data_df)
show(ARIMA_y_AIC_W)

    studentized Breusch-Pagan test

data:  arima_res ~ t
BP = 3.1663, df = 2, p-value = 0.2053
# The ADF test
ARIMA_y_AIC_ADF <- ur.df(AUTOARIMA_y_AIC[['residuals']], type="none", lags=num_lags, selectlags="Fixed")    
summary(ARIMA_y_AIC_ADF)  

############################################### 
# Augmented Dickey-Fuller Test Unit Root Test # 
############################################### 

Test regression none 


Call:
lm(formula = z.diff ~ z.lag.1 - 1)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.99093 -0.07843  0.00179  0.05962  0.44589 

Coefficients:
        Estimate Std. Error t value Pr(>|t|)    
z.lag.1 -0.97983    0.08079  -12.13   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1483 on 153 degrees of freedom
Multiple R-squared:  0.4902,    Adjusted R-squared:  0.4868 
F-statistic: 147.1 on 1 and 153 DF,  p-value: < 2.2e-16


Value of test-statistic is: -12.1286 

Critical values for test statistics: 
      1pct  5pct 10pct
tau1 -2.58 -1.95 -1.62
# The KPSS test
ARIMA_y_AIC_KPSS <- ur.kpss(AUTOARIMA_y_AIC[['residuals']], type="mu", lags="nil", use.lag=NULL)    
summary(ARIMA_y_AIC_KPSS)    # Showing the result of the test

####################### 
# KPSS Unit Root Test # 
####################### 

Test is of type: mu with 0 lags. 

Value of test-statistic is: 0.1811 

Critical value for a significance level of: 
                10pct  5pct 2.5pct  1pct
critical values 0.347 0.463  0.574 0.739
# The LB test
ARIMA_y_AIC_LB <- Box.test(AUTOARIMA_y_AIC[["residuals"]], lag=maxlag)
show(ARIMA_y_AIC_LB)

    Box-Pierce test

data:  AUTOARIMA_y_AIC[["residuals"]]
X-squared = 2.0586, df = 10, p-value = 0.9959

ARIMA (1,0,0)

AUTOARIMA_y_AIC <- arima(y, order=c(1,0,0), include.mean=FALSE, method="CSS-ML")
arima_res=AUTOARIMA_y_AIC[['residuals']]

# The studentized BP test
ARIMA_y_AIC_BP <- lmtest::bptest(formula=arima_res~t, varformula=NULL, studentize=TRUE, data=Data_df)
show(ARIMA_y_AIC_BP)

    studentized Breusch-Pagan test

data:  arima_res ~ t
BP = 0.0073459, df = 1, p-value = 0.9317
# The studentized White test
ARIMA_y_AIC_W <- lmtest::bptest(formula=arima_res~t, varformula=arima_res~t+I(t^2), studentize=TRUE, data=Data_df)
show(ARIMA_y_AIC_W)

    studentized Breusch-Pagan test

data:  arima_res ~ t
BP = 3.303, df = 2, p-value = 0.1918
# The ADF test
ARIMA_y_AIC_ADF <- ur.df(AUTOARIMA_y_AIC[['residuals']], type="none", lags=num_lags, selectlags="Fixed")    
summary(ARIMA_y_AIC_ADF)  

############################################### 
# Augmented Dickey-Fuller Test Unit Root Test # 
############################################### 

Test regression none 


Call:
lm(formula = z.diff ~ z.lag.1 - 1)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.03063 -0.07333  0.00614  0.06769  0.49961 

Coefficients:
        Estimate Std. Error t value Pr(>|t|)    
z.lag.1  -0.9797     0.0808  -12.12   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1544 on 153 degrees of freedom
Multiple R-squared:   0.49, Adjusted R-squared:  0.4866 
F-statistic:   147 on 1 and 153 DF,  p-value: < 2.2e-16


Value of test-statistic is: -12.1237 

Critical values for test statistics: 
      1pct  5pct 10pct
tau1 -2.58 -1.95 -1.62
# The KPSS test
ARIMA_y_AIC_KPSS <- ur.kpss(AUTOARIMA_y_AIC[['residuals']], type="mu", lags="nil", use.lag=NULL)    
summary(ARIMA_y_AIC_KPSS)    # Showing the result of the test

####################### 
# KPSS Unit Root Test # 
####################### 

Test is of type: mu with 0 lags. 

Value of test-statistic is: 0.1077 

Critical value for a significance level of: 
                10pct  5pct 2.5pct  1pct
critical values 0.347 0.463  0.574 0.739
# The LB test
ARIMA_y_AIC_LB <- Box.test(AUTOARIMA_y_AIC[["residuals"]], lag=maxlag)
show(ARIMA_y_AIC_LB)

    Box-Pierce test

data:  AUTOARIMA_y_AIC[["residuals"]]
X-squared = 13.975, df = 10, p-value = 0.1742

Riassumento abbiamo i seguenti p-values:

  • ARIMA (4,0,6)
    • Omoschedasticità
      • Breusch-Pagan test: 0.9801
      • White test: 0.2359
    • Stazionarietà
      • Dickey-Fuller test: < 0.01
      • Kpss test: > 0.1
    • Non correlazione
      • Ljung-Box test: 0.9726
  • ARIMA (6,0,1)
    • Omoschedasticità
      • Breusch-Pagan test: 0.7741
      • White test: 0.2053
    • Stazionarietà
      • Dickey-Fuller test: < 0.01
      • Kpss test: > 0.1
    • Non correlazione
      • Ljung-Box test: 0.9959
  • ARIMA (1,0,0)
    • Omoschedasticità
      • Breusch-Pagan test: 0.9317
      • White test: 0.1918
    • Stazionarietà
      • Dickey-Fuller test: < 0.01
      • Kpss test: > 0.1
    • Non correlazione
      • Ljung-Box test: 0.1742

In tutti i casi si ottiene un p-value elevato per il BP test indicando quindi l’omoschedasticità dei residui dei tre modelli. L’elevato p-value del LB test non ci permette di rigettare l’ipotesi nulla di residui generati da rumore bianco. Infine, il p-value basso del ADF test sta a indicare che i residui possono essere considerati stazionari con un livello di confidenza dell’1%, mentre il KPSS test, che restiuisce un valore del p-value maggiore di 0.05, indica che l’ipotesi nulla di stazionarietà non è rigettabile.

Si continuerà l’analisi utilizzando il modello ARIMA (4,0,6) essendo quello che resituisce valori migliori dei residui in termini di Omoschedasticità Stazionarietà e non correlazione, lasciando supporre che è quindi il modello in grado di descrivere in maniera migliore il remainder STL. Equazione del modello: \(X_t =Φ_1X_{t−1}+Φ_2X_{t−2}+...+Φ_4X_{t−4}−Θ_1W_{t−1}−Θ_2W_{t−2}−...−Θ_6W_{t−6}+ W_ t\)

y <- y_BCT_log_STL_def_win_dcmp_ts$remainder
AUTOARIMA_y_AIC <- arima(y, order=c(4,0,6), include.mean=FALSE, method="CSS-ML")
autoplot(ts(fitted(AUTOARIMA_y_AIC) , start=c(2009, 01), end=c(2021, 11), frequency = 12), series= "arima") +
  autolayer(ts(y_BCT_log_STL_remainder , start=c(2009, 01), end=c(2021, 11), frequency = 12), series="remainder") +
  xlab("Month") + ylab("rem") +
  ggtitle("Remainder Toyota Camry") +
  guides(colour=guide_legend(title="Legend"))

Per completezza vediamo anche gli autocorrelorammi.

Data_df <- Camry_TrnS_df
y <- resid(AUTOARIMA_y_AIC)
#class(y)
T <- length(y)
# maxlag <- ceiling(10*log10(T))    # Default
# maxlag <- ceiling(sqrt(n)+45)     # Box-Jenkins
maxlag <- ceiling(min(10, T/4))     # Hyndman (for data without seasonality)
# maxlag <- ceiling(min(2*12, T/5)) # Hyndman (for data with seasonality)
# https://robjhyndman.com/hyndsight/ljung-box-test/
Aut_Fun_y <- acf(y, lag.max=maxlag, type="correlation", plot=FALSE)
ci_90 <- qnorm((1+0.90)/2)/sqrt(T)
ci_95 <- qnorm((1+0.95)/2)/sqrt(T)
ci_99 <- qnorm((1+0.99)/2)/sqrt(T)
Plot_Aut_Fun_y <- data.frame(lag=Aut_Fun_y$lag, acf=Aut_Fun_y$acf)
First_Date <- paste(Data_df$Month[1],Data_df$Year[1])
Last_Date <- paste(Data_df$Month[T],Data_df$Year[T])
title_content <- bquote(atop("Autocorrelogram of the Residuals of the ARIMA model for the Remainders in the STL Decomp. for the Log Box-Cox Transf. of the Training Set from ", .(First_Date), " to ", .(Last_Date)))
subtitle_content <- bquote(paste("Path length ", .(T), " sample points. Lags ", .(maxlag)))
caption_content <- "Author: Matteo Chiacchia"
x_name <- bquote("lags")
x_breaks_num <- maxlag
x_binwidth <- 1
x_breaks <- Aut_Fun_y$lag
x_labs <- format(x_breaks, scientific=FALSE)

ggplot(Plot_Aut_Fun_y, aes(x=lag, y=acf))+
  geom_segment(aes(x=lag, y=rep(0,length(lag)), xend=lag, yend=acf), linewidth=1, col="black") +
  # geom_col(mapping=NULL, data=NULL, position="dodge", width=0.1, col="black", inherit.aes=TRUE)+
  geom_hline(aes(yintercept=-ci_90, color="CI_90"), show.legend=TRUE, lty=3) +
  geom_hline(aes(yintercept=ci_90, color="CI_90"), lty=3) +
  geom_hline(aes(yintercept=ci_95, color="CI_95"), show.legend=TRUE, lty=4)+
  geom_hline(aes(yintercept=-ci_95, color="CI_95"), lty=4) +
  geom_hline(aes(yintercept=-ci_99, color="CI_99"), show.legend=TRUE, lty=4) +
  geom_hline(aes(yintercept=ci_99, color="CI_99"), lty=4) +
  scale_x_continuous(name="lag", breaks=x_breaks, label=x_labs) +
  scale_y_continuous(name="acf value", breaks=waiver(), labels=NULL,
                     sec.axis=sec_axis(~., breaks=waiver(), labels=waiver())) +
  scale_color_manual(name="Conf. Inter.", labels=c("90%","95%","99%"),
                     values=c(CI_90="green", CI_95="blue", CI_99="red")) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  theme(plot.title=element_text(hjust=0.5, size=8), 
        plot.subtitle=element_text(hjust= 0.5, size=7.5),
        plot.caption=element_text(hjust=1.0),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")

# The partial autocorrelogram of the remainders
Data_df <- Camry_TrnS_df
y <- AUTOARIMA_y_AIC[["residuals"]]
T <- length(y)
# maxlag <- ceiling(10*log10(T))    # Default
# maxlag <- ceiling(sqrt(n)+45)     # Box-Jenkins
maxlag <- ceiling(min(10, T/4))     # Hyndman (for data without seasonality)
# maxlag <- ceiling(min(2*12, T/5)) # Hyndman (for data with seasonality) 
# https://robjhyndman.com/hyndsight/ljung-box-test/
Part_Aut_Fun_y <- pacf(y, lag.max=maxlag, plot=FALSE)
ci_90 <- qnorm((1+0.90)/2)/sqrt(T)
ci_95 <- qnorm((1+0.95)/2)/sqrt(T)
ci_99 <- qnorm((1+0.99)/2)/sqrt(T)
Plot_Part_Aut_Fun_y <- data.frame(lag=Part_Aut_Fun_y$lag, pacf=Part_Aut_Fun_y$acf)
title_content <- bquote(atop("Partial Autocorrelogram of the Residuals of the ARIMA model for the Remainders in the STL Decomp. for the Log Box-Cox Transf. of the Training Set from ", .(First_Date), " to ", .(Last_Date)))
subtitle_content <- bquote(paste("Path length ", .(T), " sample points. Lags ", .(maxlag)))
caption_content <- "Author: Matteo Chiacchia"
x_name <- bquote("lags")
x_breaks_num <- maxlag
x_binwidth <- 1
x_breaks <- Part_Aut_Fun_y$lag
x_labs <- format(x_breaks, scientific=FALSE)
ggplot(Plot_Part_Aut_Fun_y, aes(x=lag, y=pacf))+
  geom_segment(aes(x=lag, y=rep(0,length(lag)), xend=lag, yend=pacf), linewidth=1, col="black") +
  # geom_col(mapping=NULL, data=NULL, position="dodge", width=0.1, col="black", inherit.aes=TRUE)+
  geom_hline(aes(yintercept=-ci_90, color="CI_90"), show.legend=TRUE, lty=3) +
  geom_hline(aes(yintercept=ci_90, color="CI_90"), lty=3) +
  geom_hline(aes(yintercept=ci_95, color="CI_95"), show.legend=TRUE, lty=4)+
  geom_hline(aes(yintercept=-ci_95, color="CI_95"), lty=4) +
  geom_hline(aes(yintercept=-ci_99, color="CI_99"), show.legend=TRUE, lty=4) +
  geom_hline(aes(yintercept=ci_99, color="CI_99"), lty=4) +
  scale_x_continuous(name="lag", breaks=x_breaks, label=x_labs) +
  scale_y_continuous(name="pacf value", breaks=waiver(), labels=NULL,
                     sec.axis=sec_axis(~., breaks=waiver(), labels=waiver())) +
  scale_color_manual(name="Conf. Inter.", labels=c("90%","95%","99%"),
                     values=c(CI_90="red", CI_95="blue", CI_99="green")) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  theme(plot.title=element_text(hjust=0.5, size=8), 
        plot.subtitle=element_text(hjust= 0.5, size=8.5),
        plot.caption=element_text(hjust=1.0),
        legend.key.width=unit(0.8,"cm"), legend.position="bottom")

Il numero di picchi corrispondenti ai lags positivi che attraversano le linee di confidenza è nullo. confermando il risultato ottenuto dal LB test.

COVID-19: La pandemia di COVID-19 ha causato una serie di cambiamenti nell’economia, nella società e nella vita quotidiana delle persone. Ciò ha comportato una serie di conseguenze che possono influenzare i modelli delle time series. Ad esempio, le chiusure forzate delle attività economiche, le interruzioni nella catena di approvvigionamento e le fluttuazioni nei comportamenti di spesa dei consumatori possono influire sui dati che alimentano questi modelli.

Modificare il valore del noise di un modello di time series può aiutare a tener conto di queste fluttuazioni impreviste e delle incertezze causate dalla pandemia. Ciò può essere particolarmente importante per la previsione della domanda di mercato, per la valutazione delle risorse finanziarie necessarie e per la pianificazione a lungo termine. Tuttavia, la modifica del valore del noise deve essere supportata da una solida analisi dei dati e della situazione attuale per garantire che le previsioni siano il più accurate possibile.

Secondo i dati dell’Associazione Nazionale dei Rivenditori di Auto (NADA), le vendite di auto negli Stati Uniti sono diminuite del 14,6% nel 2020 rispetto all’anno precedente, con un totale di circa 14,5 milioni di auto vendute rispetto ai 17 milioni di auto vendute nel 2019.

La pandemia ha avuto un impatto significativo sul settore automobilistico degli Stati Uniti, con molte fabbriche e concessionarie che hanno chiuso temporaneamente durante la prima ondata della pandemia. Inoltre, la situazione economica incerta ha portato molti consumatori a posticipare l’acquisto di un’auto o a optare per opzioni di trasporto alternative come il car sharing o il bike sharing.

Tuttavia, le vendite di auto negli Stati Uniti hanno mostrato una ripresa nella seconda metà del 2020. Vediamo adesso il lineplot relativo ai residui del modello ARIMA.

autoplot( AUTOARIMA_y_AIC[["residuals"]])

Dal lineplot si nota immediatamente la presenza di un valore anomalo. Per poter trattare meglio, quindi, il noise e poter effettivamente affermare di avere un rumore che non dipende da nessuna causa esterna, modifichiamo il valore di picco più basso tramite un’interpolazione lineare.

Camry_TrnS_df <- add_column(Camry_TrnS_df, stl_residuals=AUTOARIMA_y_AIC[["residuals"]])
#Covid value modify
y <- AUTOARIMA_y_AIC[["residuals"]]
min_covid <- min(y)
min_covid_index <- which.min(y)
month_year_min_covid = sprintf("%s/%s", Camry_TrnS_df$Month[min_covid_index], Camry_TrnS_df$Year[min_covid_index])
cat(month_year_min_covid)
4/2020
y[y == min_covid] <- 0
y[min_covid_index] <- ((y[min_covid_index+1] -  y[min_covid_index-1]) / 2) +  y[min_covid_index-1]
y_rem_ARIMA_residuals <- y

Una volta modificato il valore relativo al crollo delle vendite a causa della pandemia del COVID-19, si procede con l’analisi dei residui, cercando di verificare quale possa essere la distribuzione generatrice.

Per ottenere informazioni sulla distribuzione che meglio rappresenta i residui del modello ARMA, possiamo considerare il grafico di Cullen-Frey della skewness e della kurtosis dei dati.

y <- as.vector(y_rem_ARIMA_residuals)
descdist(y, discrete=FALSE, method="sample", graph=TRUE, boot=1000)
summary statistics
------
min:  -0.5911887   max:  0.4226409 
median:  0.0005658722 
mean:  -0.00428444 
sample sd:  0.1162963 
sample skewness:  -0.9200748 
sample kurtosis:  8.157539 

Nel grafico il punto blu rappresenta la coppia skewness-kurtosis dei dati osservati (cioè la skewness e la kurtosis dei residui del modello ARMA), e i punti arancioni rappresentano le coppie skewness-kurtosis di 1000 campionamenti dei dati osservati ottenuti con il metodo del bootstrap. Sul grafico sono riportati con diversi simboli e linee (come spiegato nella legenda) la skewness e la kurtosis di alcune distribuzioni note, come la normale, la gamma, l’uniforme, ecc. I punti di bootstrap sono vicini alle distribuzioni logistica e T-Student. Pertanto, queste due distribuzioni potrebbero essere testate per valutare quale meglio rappresenti i dati. Calcoliamo anche i valori di skewness e kurtosis con relativi intervalli di confidenza.

y <- y_rem_ARIMA_residuals
y_skew <- DescTools::Skew(y, weights=NULL, na.rm=TRUE, method=2, conf.level=0.90, ci.type="bca", R=1000) 
show(y_skew)
      skew     lwr.ci     upr.ci 
-0.9290902 -2.0126175  0.1589242 
y <- y_rem_ARIMA_residuals
y_kurt <- DescTools::Kurt(y, weights=NULL, na.rm=TRUE, method=2, conf.level=0.90, ci.type="bca", R=1000) 
show(y_kurt)
     kurt    lwr.ci    upr.ci 
 5.367591  2.581851 10.763979 

Notiamo che la skewness nulla ricade all’interno dell’intervallo di confidenza del 90%, possiamo quindi supporre che i residui abbiano una distribuzione simmetrica. Per quanto riguarda la kurtosis possiamo supporre che la distribuzione sia leptocurtica ma non può essere una distribuzione logistica essendo il valore del suo eccesso di kurtosis (\(\frac{6}{5}\)) non rientrante all’interno dell’intervallo di confidenza calcolato.

Eseguiamo inoltre i test di gaussianità:

  • Shapiro-Wilks
  • D’Agostino-Pearson
  • Anderson-Darling
  • Jarque-Bera
# Shapiro-Wilks (*SW*) test.
y <- y_rem_ARIMA_residuals
y_SW <- shapiro.test(y)
show(y_SW)

    Shapiro-Wilk normality test

data:  y
W = 0.92528, p-value = 3.263e-07
# D'Agostino Pearson (*DP*) test.
y <- y_rem_ARIMA_residuals
y_DP <- dagoTest(y)
show(y_DP)

Title:
 D'Agostino Normality Test

Test Results:
  STATISTIC:
    Chi2 | Omnibus: 43.573
    Z3  | Skewness: -4.2576
    Z4  | Kurtosis: 5.0444
  P VALUE:
    Omnibus  Test: 3.453e-10 
    Skewness Test: 2.066e-05 
    Kurtosis Test: 4.55e-07 

Description:
 Fri Mar 24 12:52:45 2023 by user: 
# Anderson-Darling (AD) test.
y <- y_rem_ARIMA_residuals
y_AD <- ad.test(y)
show(y_AD)

    Anderson-Darling test of goodness-of-fit
    Null hypothesis: uniform distribution
    Parameters assumed to be fixed

data:  y
An = Inf, p-value = 3.871e-06
# Jarque-Bera (*JB*) test.
y <- y_rem_ARIMA_residuals
y_JB <- jarque.bera.test(y)
show(y_JB)

    Jarque Bera Test

data:  y
X-squared = 193.66, df = 2, p-value < 2.2e-16

Tutti i test resituiscono un forte rigetto dell’ipotesi nulla di gaussianità.

Si passa, quindi, alla stima dei parametri della distribuzione T-Student Preliminarmente effettuiamo le seguenti operazioni:

  • Standardizziamo i residui ovvero trasformarli in modo che abbiano media zero e deviazione standard pari a uno
  • Calcoliamo i quantili empirici ovvero identifichiamo i valori che dividono la distribuzione dei residui in percentuali uguali.
  • Calcoliamo la distribuzione empirica ovvero una stima della distribuzione reale dei residui.
  • Calcoliamo la probabilità empirica.

Ciò verrà usato per confrontare le caratteristiche della distribuzione empirica dei residui con le caratteristiche dela distribuzione che stimeremo li abbia generati.

z <- as.vector(y_rem_ARIMA_residuals)
z_st <- (1/sd(z))*as.vector(z-mean(z)) # We standardize the residuals of the ARIMA model.
z_st_qemp <- qemp(ppoints(z_st), z_st) # The empirical quantiles of the residuals.
z_st_demp <- demp(z_st_qemp, z_st)     # The empirical probability density of the residuals.
z_st_pemp <- pemp(z_st_qemp, z_st)     # The empirical probability distribution of the residuals.  
x <- z_st_qemp
y_d <- z_st_demp
y_p <- z_st_pemp
hist(z_st, col="cyan", border="black", xlim=c(x[1]-1.0, x[length(x)]+1.0), ylim=c(0, y_d[length(y)]+0.75), 
     freq=FALSE, main="Density Histogram and Empirical Density Function of the Standardized Residuals of the 
     ARIMA model", 
     xlab="Standardized Residuals", ylab="Histogram Values+Density Function")
lines(x, y_d, lwd=2, col="darkblue")

Definiamo l’adattamento a una Student. Per procedere definiamo ora le funzioni caratteristiche di una Student generalizzata, non disponibili sulle librerie di R, ma costruibili a partire da trasformazioni standard delle funzioni analoghe della distribuzione standard di Student disponibili su R:

  • La funzione dt_ls restituisce il valore della funzione di densità di probabilità della distribuzione generalizzata di Student a partire dalla densità dt della distribuzione di Student Standard
  • La funzione pt_ls restituisce il valore della funzione di densità cumulativa della distribuzione generalizzata di Student a partire dalla densità cumulata pt della distribuzione di Student Standard
  • La funzione qt_ls restituisce il valore della funzione di densità cumulativa inversa della distribuzione generalizzata di Student a partire dalla densità cumulata inversa qt della distribuzione di Student Standard
  • La funzione rt_ls genera un vettore di variabili casuali distribuiti secondo la distribuzione generalizzata di Student a partire da un vettore di variabili casuali che seguono la distribuzione di Student Standard

L’introduzione di queste funzioni consente di stimare i parametri della ipotetica distribuzione generalizzata di Student che genera i residui mediante la funzione fitdistr della libreria MASS.

dt_ls <- function(x, m, s, df)  1/s*dt((x-m)/s, df)
pt_ls <- function(q, m, s, df)  pt((q-m)/s, df)
qt_ls <- function(p, m, s, df)  qt(p, df)*s+m
rt_ls <- function(n, m, s, df)  rt(n,df)*s+m

In questo caso, il parametro di location m coincide con la media della distribuzione Student generalizzata, ma il parametro di scale s è dato da s= \(\sigma\sqrt{\frac{df-2}{df}}\) dove \(\sigma\) è il parametro di deviazione standard della distribuzione. Pertanto, poiché assumiamo come punto di partenza df=3, la scelta naturale per il parametro di locaction è m=0 e per il parametro di scale è s=\(\sqrt{\frac{1}{3}}\).

fitdist_t_ls <- fitdist(z_st, "t_ls", start=list(m=0, s=sqrt(1/3), df=3), method="mle")
fitdist_t_ls_m <- as.numeric(fitdist_t_ls$estimate[1])
fitdist_t_ls_s <- as.numeric(fitdist_t_ls$estimate[2])
fitdist_t_ls_df <- as.numeric(fitdist_t_ls$estimate[3])
summary(fitdist_t_ls)
Fitting of the distribution ' t_ls ' by maximum likelihood 
Parameters : 
Loglikelihood:  -203.9885   AIC:  413.9771   BIC:  423.1073 
Correlation matrix:
             m           s          df
m   1.00000000 -0.08908302 -0.09586272
s  -0.08908302  1.00000000  0.65083064
df -0.09586272  0.65083064  1.00000000

Si traccia il grafico della densità stimata della Student generalizzata insieme all’istogramma e alla densità empirica.

hist(z_st, col="green", border="black", xlim=c(x[1]-2.0, x[length(x)]+2.0), ylim=c(0, y_d[length(y_d)]+0.75), 
     freq=FALSE, main="Density Histogram of the Standardized Residuals of the ARIMA model+Empirical 
     Density+Estimated Generalized Student Density", xlab="Standardized Residuals", ylab="Density")
lines(density(z_st), lwd=2, col="darkgreen")
lines(x, dt_ls(x, m=fitdist_t_ls_m, s=fitdist_t_ls_s, df=fitdist_t_ls_df), lwd=2, col="blue")
legend("topleft", legend=c("Empirical Density", "Estimated Density"), col=c("darkgreen", "blue"), 
       lty=1, lwd=0.1, cex=0.8, x.intersp=0.50, y.intersp=0.40, text.width=2, seg.len=1, text.font=4, box.lty=0,
       inset=-0.01, bty="n")

Si traccia il grafico CDF (cumulative distribution function) per confrontare la funzione di distribuzione cumulativa empirica con quella stimata dalla distribuzione di probabilità di Student. Questo permette di valutare se la distribuzione di probabilità stimata si adatta bene ai dati empirici.

cdfcomp(fitdist_t_ls)

Si tracciano anche Q-Q plot e P-P plot.

  • Il grafico Q-Q plot confronta i quantili della distribuzione di probabilità empirica con quelli della distribuzione teorica o stimata. Questo permette di valutare se le due distribuzioni sono simili o se differiscono significativamente tra loro. Se le due distribuzioni sono simili, i punti sul grafico Q-Q plot si adatteranno a una retta diagonale. Se le due distribuzioni differiscono, i punti sul grafico Q-Q plot si discosteranno dalla retta diagonale.

  • Il grafico P-P plot confronta le funzioni di distribuzione cumulativa della distribuzione di probabilità empirica con quella della distribuzione teorica o stimata. Anche in questo caso, se le due distribuzioni sono simili, i punti sul grafico P-P plot si adatteranno a una retta diagonale. Se le due distribuzioni differiscono, i punti sul grafico P-P plot si discosteranno dalla retta diagonale.

In generale, i grafici Q-Q plot e P-P plot vengono utilizzati per valutare la bontà di adattamento di una distribuzione teorica o stimata ai dati empirici. Se le due distribuzioni sono simili, la distribuzione teorica o stimata può essere considerata un buon modello per i dati. Se le due distribuzioni differiscono significativamente, è necessario considerare l’utilizzo di un modello diverso o di modificare i parametri del modello.

par(mfrow=c(1,2))
qqcomp(fitdist_t_ls)
ppcomp(fitdist_t_ls)
par(mfrow=c(1,1))

Si traccia anche un Q-Q plot più dettagliato.

x <- z_st_qemp
fitdist_t_ls_m <- as.numeric(fitdist_t_ls$estimate[1])
fitdist_t_ls_s <- as.numeric(fitdist_t_ls$estimate[2])
fitdist_t_ls_df <- as.numeric(fitdist_t_ls$estimate[3])
car::qqPlot(x, distribution ="t_ls", m=fitdist_t_ls_m, s=fitdist_t_ls_s, df=fitdist_t_ls_df,
            line="robust", 
            col=carPalette()[2], col.lines=carPalette()[8],
            pch=16, cex=0.5, las=1,
            main=bquote(atop("QQ-plot of the Standardized Residuals of the ARIMA Model for the Detrended and Deseasonalized Component",
                             paste("of the Box-Cox Transformation of the Monthly Toyota Camry Sales Time Series Against the Generalized Student Distribution with Location ", .(fitdist_t_ls_m),", Scale ", .(fitdist_t_ls_s),", and Degrees of Freedom ", .(fitdist_t_ls_df),"."))),
            xlab=bquote(paste("Theoretical Quantiles of the Generalized Student Distribution")), 
            ylab="Quantiles of the Empirical Distribution of the Residuals")
[1] 1 2
# adding the interquartile line, corresponding to the option "quartiles" of the parameter line.
probs <- c(0.25,0.75)
quant_x <- as.vector(quantile(x, probs))
quant_t <- qt_ls(probs, m=fitdist_t_ls_m, s=fitdist_t_ls_s, df=fitdist_t_ls_df)
slope <- diff(quant_x)/diff(quant_t)
int <- quant_x[1]-slope*quant_t[1]
abline(a=int, b=slope, col="black", lwd=1)
# adding the first bisector of the axis line, corresponding to the option "0-1" of the parameter qq.line.type.
abline(a=0, b=1, col="green", lwd=1)
legend("topleft",
       legend=c("robust regression line", "interquartile line", "y=x line"),
       col=c("red", "black", "green"),
       lty=1, lwd=0.1,
       cex=0.80, x.intersp=0.50, y.intersp=0.40, text.width=2, seg.len=1,
       inset=-0.01, bty="n")

Adesso l’idea è eseguire il bootstrapping per stimare l’incertezza nei parametri adattati.

Innanzitutto, adattiamo una distribuzione t di Student con media zero al dataset standardizzato z_st.

Successivamente, si utilizza la funzione bootdist per generare 1.000 campioni bootstrap della distribuzione adattata e stimare l’incertezza nelle stime dei parametri. Si estrae il valore mediano delle stime dei parametri dai campioni bootstrap e si arrotondano, salvandoli nella variabile student_zero_mean_params.

fitdist_t_ls_zero_mean <- fitdist(z_st, "t_ls", start=list(s=sqrt(1/3), df=3), method="mle", fix.arg=list(m=0))
summary(fitdist_t_ls_zero_mean)
Fitting of the distribution ' t_ls ' by maximum likelihood 
Parameters : 
Fixed parameters:
Loglikelihood:  -204.4247   AIC:  412.8494   BIC:  418.9363 
Correlation matrix:
           s        df
s  1.0000000 0.6441575
df 0.6441575 1.0000000
# Again, the *bootdist* function the library *fitdistrplus* allows to evaluate the uncertainty in estimated parameters of the fitted distribution.
fitdist_t_ls_zero_mean_bd <- bootdist(fitdist_t_ls_zero_mean, niter=1000)
summary(fitdist_t_ls_zero_mean_bd)
Parametric bootstrap medians and 95% percentile CI 
      Median      2.5%      97.5%
s  0.6736642 0.5462319  0.8255334
df 3.6473902 2.2267854 10.5677659
# In this case we extract and round directly the median value of the parameters till the 2th decimal digit. 
fitdist_t_ls_zero_mean_bd_med <- c(median(fitdist_t_ls_zero_mean_bd$estim$s), median(fitdist_t_ls_zero_mean_bd$estim$df))
show(fitdist_t_ls_zero_mean_bd_med)
[1] 0.6736642 3.6473902
student_zero_mean_params <- round(fitdist_t_ls_zero_mean_bd_med, 2)
show(student_zero_mean_params)
[1] 0.67 3.65

Si utilizzano, quindi, i seguenti test per valutare la bontà di adattamento della distribuzione di probabilità stimata:

  • Kolmogorov-Smirnov test
  • Cramer-Von Mises test
  • Anderson-Darling test

Kolmogorov-Smirnov test

# The Kolmogorov-Smirnov test in the library *stats*
KS_x_st_t_ls <- ks.test(x, y="pt_ls", m=0, s=student_zero_mean_params[1], df=student_zero_mean_params[2], alternative="two.sided")
show(KS_x_st_t_ls)

    Asymptotic one-sample Kolmogorov-Smirnov test

data:  x
D = 0.066865, p-value = 0.4923
alternative hypothesis: two-sided

Cramer-Von Mises test

# The Cramer-Von Mises test in the library *goftest*.
# This function performs the Cramer-Von Mises test of goodness-of-fit to the distribution specified by the 
# argument null. It is assumed that the values in x are independent and identically distributed random values, 
# with some cumulative distribution function F. The null hypothesis is that F is the function specified by the 
# argument null, while the alternative hypothesis is that F is some other function.
CVM_x_st_t_ls <- cvm.test(z_st, null="pt_ls", m=0, s=student_zero_mean_params[1], df=student_zero_mean_params[2], estimated=FALSE)
show(CVM_x_st_t_ls)

    Cramer-von Mises test of goodness-of-fit
    Null hypothesis: distribution ‘pt_ls’
    with parameters m = 0, s = 0.67, df = 3.65
    Parameters assumed to be fixed

data:  z_st
omega2 = 0.10191, p-value = 0.5762

Anderson-Darling test

# The Anderson-Darling test in the library *goftest*.
AD_x_st_t_ls <- ad.test(z_st, "pt_ls", m=0, s=student_zero_mean_params[1], df=student_zero_mean_params[2], estimated=FALSE)
show(AD_x_st_t_ls)

    Anderson-Darling test of goodness-of-fit
    Null hypothesis: distribution ‘pt_ls’
    with parameters m = 0, s = 0.67, df = 3.65
    Parameters assumed to be fixed

data:  z_st
An = 0.50535, p-value = 0.7411

Dai p-values ottenuti si nota che la distribuzione di Student stimata si adatta abbastanza bene ai dati, di conseguenza si utilizzerà per la creazione degli intervalli di confidenza.

2. Forecast

Per eseguire il forecast del modello eseguiamo le predizioni su ogni singola componente (remainder, trend, seasonality) per poi unirle in maniera additiva. Si calcolano inoltre le bande di predizione (80% e 90%) del rumore.

Remainder forecast

Per effettuare la predizione del remainder si utilizza il modello ARIMA trovato in precedenza. Le bande di predizione, invece, vengono calcolate mediante l’utilizzo dei quantili della distribuzione di Student al 5%, 20%, 80%, 95% moltiplicati per lo standard error della previsione del modello ARIMA. Il quantile rappresenta il valore al quale la probabilità cumulativa della distribuzione è uguale al livello di confidenza desiderato.

L’errore standard della previsione rappresenta la deviazione standard dell’errore di previsione del modello ARIMA. In altre parole, rappresenta la misura della dispersione dei dati intorno alla linea di previsione.

Moltiplicando il quantile della distribuzione di Student per l’errore standard della previsione, si ottiene una stima dell’ampiezza dell’intervallo di confidenza per la previsione. Questo viene quindi sommato alla previsione media per ottenere le bande inferiori e superiori della previsione al livello di confidenza desiderato.

y <- y_BCT_log_STL_remainder
AUTOARIMA_y_AIC <- arima(y, order=c(4,0,6), include.mean=FALSE, method="CSS-ML")
AUTOARIMA_y_AIC_pred <- predict(AUTOARIMA_y_AIC, n.ahead = TstS_length)
y_rem_pred_for <- forecast::forecast(AUTOARIMA_y_AIC, h=TstS_length, level = c(80, 95), bootstrap = TRUE)
autoplot(y_rem_pred_for)

y_rem_pred_for_mean <- y_rem_pred_for[["mean"]]
y_res_log_pred_RH_for_080_low_int <- (y_rem_pred_for_mean
                                       +qt_ls(0.20, m=0, s=student_zero_mean_params[1], 
                                             df=student_zero_mean_params[2])*AUTOARIMA_y_AIC_pred$se)

y_res_log_pred_RH_for_080_upp_int <- (y_rem_pred_for_mean
                                       +qt_ls(0.80, m=0, s=student_zero_mean_params[1],
                                             df=student_zero_mean_params[2])*AUTOARIMA_y_AIC_pred$se)

y_res_log_pred_RH_for_095_low_int <- (y_rem_pred_for_mean
                                       +qt_ls(0.05, m=0, s=student_zero_mean_params[1],
                                             df=student_zero_mean_params[2])*AUTOARIMA_y_AIC_pred$se)
y_res_log_pred_RH_for_095_upp_int <- (y_rem_pred_for_mean
                                       +qt_ls(0.95, m=0, s=student_zero_mean_params[1],
                                             df=student_zero_mean_params[2])*AUTOARIMA_y_AIC_pred$se)

Si visualizza inzialmente la funzione di previsione che utilizza il metodo random walk e calcola autmatica gli intervalli di confidenza.

sales_log <- as.vector(log(df$sales))
sales_log_test <- sales_log[c((TrnS_length+1):(TrnS_length+TstS_length))]
sales_log_stl <- stl(Camry_TrnS_ts, s.window=11, t.window = 29, robust = TRUE)
sales_log_stl_for <- forecast::forecast(sales_log_stl, h=TstS_length, method="naive", level=c(80,95))
plot(sales_log_stl_for)

autoplot(ts(log(df_Camry_US_sales$sales), start=c(2009, 01), end=c(2022, 11), frequency = 12), serie ="Toyota Camry path") +
  autolayer(ts(sales_log_stl_for[["mean"]], start=c(2021, 12), end=c(2022, 11), frequency = 12), series="Forecast with stl func") +
  xlab("Month") + ylab("Sales") +
  ggtitle("Forecast Toyota Camry sales") +
  guides(colour=guide_legend(title="Legend"))

Trend forecast

Per effettuare il forecast del trend si è ricorso a vari metodi:

  • Naive forecast
  • Holt Winters forecast
  • Gran mean forecast
Naive forecast

La tecnica di forecast naive o random walk è un semplice metodo di previsione delle serie temporali che assume che il valore futuro di una serie temporale sarà lo stesso del valore più recente osservato. Di conseguenza si considera come trend futuro il continuo costante dell’ultimo valore del trend.

TS_length <- TrnS_length+TstS_length
y <- y_BCT_log_STL_def_win_dcmp_ts$trend
y_trend_naive_pred <- rep(y[length(y)],TstS_length)
y_trend_naive_for <- c(y, y_trend_naive_pred)
x <- as.Date(df$date)
y <- y_trend_naive_for
plot(x[1:TrnS_length],y[1:TrnS_length], type="l", lty = 1, lwd=1.0, col="black", xaxt = "n", 
     xlim=c(as.Date(df$date)[1],as.Date(df$date)[TS_length]),
     xlab="", ylab="Trend", 
     main="Naive Forecast of the Trend Component of the STL Decomposition of the Log Box-Cox Transformation",
     cex.main=0.95)
lines(x[TrnS_length:TS_length], y[TrnS_length:TS_length], type="l", lty = 1, lwd=1.5, col="blue")
axis(1, at=x[c(seq(1,TS_length,by=6),TS_length)], labels=format(x[c(seq(1,TS_length,by=6),TS_length)], "%Y %m"),
     tick=TRUE, cex.axis = .5)
legend("topright", inset=c(0.05,0.0), legend=c("Trend Component", "Naive Forecast"), col=c("black", "blue"),
       lty=1, lwd=0.1, cex=0.7, x.intersp=0.50, y.intersp=0.40, text.width=2, seg.len=1, text.font=4, box.lty=0, 
       bty="n")

Holt-Winters forecast

Holt Winters linear trend method è una tecnica di previsione delle serie temporali che utilizza un modello lineare per la componente del trend. Per il trend, il metodo di Holt-Winters utilizza un modello di regressione lineare per stimare il trend futuro. In particolare, l’equazione del trend lineare è espressa come:

\(T(t+1) = T(t) + b\)

dove \(T(t+1)\) rappresenta la previsione del trend per il periodo successivo, \(T(t)\) rappresenta il trend attuale e b rappresenta la pendenza della linea di trend.

y <- y_BCT_log_STL_def_win_dcmp_ts$trend
y_trend_HW_pred <- holt(y, h=TstS_length)
y_trend_HW_pred_mean <- as.numeric(y_trend_HW_pred$mean)
y_trend_HW_for <- c(y,y_trend_HW_pred_mean)
x <- as.Date(df$date)
y <- y_trend_HW_for
plot(x[1:TrnS_length],y[1:TrnS_length], type="l", col="black", xaxt = "n", 
     xlim=c(as.Date(df$date)[1],as.Date(df$date)[TS_length]),
     xlab="", ylab="Trend", 
    main="Holt Winters Forecast of the Trend Component of the STL Decomposition of the Log Box-Cox Transformation",
    cex.main=0.95)
lines(x[TrnS_length:TS_length], y[TrnS_length:TS_length], type="l", lty = 1, lwd=1.5, col="blue")
axis(1, at=x[c(seq(1,TS_length,by=6),TS_length)], labels=format(x[c(seq(1,TS_length,by=6),TS_length)], "%Y %m"),
     tick=TRUE, cex.axis = .5)
legend("topright", inset=c(0.06,0.0), legend=c("Trend Component", "Holt Winter Forecast"), col=c("black", "blue"), lty=1, 
       lwd=0.1, cex=0.7, x.intersp=0.50, y.intersp=0.40, text.width=2, seg.len=1, text.font=4, box.lty=0, bty="n")

Gran Mean forecast

La terza tecnica consiste nel considerare la gran media della componente di trend, che è la media della componente di trend del train set continuata in maniera costante per tutta la lunghezza del Test set.

y <- y_BCT_log_STL_def_win_dcmp_ts$trend
y_gran_mean_pred <- rep(mean(y),TstS_length)
y_gran_mean_for <- c(y, rep(mean(y),TstS_length))
tail(y_gran_mean_for,30)
 [1] 10.24472 10.24680 10.24774 10.24869 10.24964 10.24462 10.23959 10.23457 10.22650 10.21844 10.21038 10.20117 10.19196 10.18276
[15] 10.17226 10.16176 10.15126 10.13949 10.31254 10.31254 10.31254 10.31254 10.31254 10.31254 10.31254 10.31254 10.31254 10.31254
[29] 10.31254 10.31254
x <- as.Date(df$date)
y <- y_gran_mean_for
plot(x[1:TrnS_length],y[1:TrnS_length], type="l", col="black", xaxt = "n", 
     xlim=c(as.Date(df$date)[1],as.Date(df$date)[TS_length]),
     xlab="", ylab="Trend", 
    main="Holt Winter Forecast of the Trend Component of the STL Decomposition of the Log Box-Cox Transformation",
    cex.main=0.95)
lines(x[TrnS_length:TS_length], y[TrnS_length:TS_length], type="l", lty = 1, lwd=1.5, col="blue")
axis(1, at=x[c(seq(1,TS_length,by=6),TS_length)], labels=format(x[c(seq(1,TS_length,by=6),TS_length)], "%Y %m"),
     tick=TRUE, cex.axis = .5)
legend("topright", inset=c(0.06,0.0), legend=c("Trend Component", "Gran Mean Forecast"), col=c("black", "blue"), lty=1, 
       lwd=0.1, cex=0.7, x.intersp=0.50, y.intersp=0.40, text.width=2, seg.len=1, text.font=4, box.lty=0, bty="n")

Seasonality forecast

Per simulare la componente stagionale per i prossimi 12 mesi è bastato prolungare la componente stagionale ripetendo per i mesi futuri i valori assunti nei mesi passati corrispondenti (naive method).

y_seas <- y_BCT_log_STL_def_win_dcmp_ts$`season_1 year`
y_seas_naive_pred <- c(y_seas[(length(y_seas)-11):length(y_seas)])
y_seas_naive_for <- c(y_seas, y_seas_naive_pred)
x <- as.Date(df$date)
y <- y_seas_naive_for
plot(x[1:TrnS_length],y[1:TrnS_length], type="l", col="black", xaxt = "n", xlab="", 
     xlim=c(as.Date(df$date)[1],as.Date(df$date)[TS_length]),
     ylab="Seasonal", 
     main="Seasonal Naive Forecast of the Seasonal Component of the STL Decomposition of the Log Box-Cox Transformation",
     cex.main=0.95)
lines(x[TrnS_length:TS_length], y[TrnS_length:TS_length], type="l", lty = 1, lwd=1.5, col="blue")
axis(1, at=x[c(seq(1,TS_length,by=6),TS_length)], labels=format(x[c(seq(1,TS_length,by=6),TS_length)], "%Y %m"),
     tick=TRUE, cex.axis = .5)
legend("topright", inset=c(0.12,0.0), legend=c("Seasonal Component", "Seasonal Naive Forecast"), col=c("black", "blue"), lty=1, 
       lwd=0.1, cex=0.7, x.intersp=0.50, y.intersp=0.40, text.width=2, seg.len=1, text.font=4, box.lty=0, bty="n")

Total forecast with naive method

Una volta applicati i vari metodi di predizione, si procede unendo in maniera additiva i valori trovati e si utilizzeranno metriche di accuratezza per verificare quali tipi di predizioni restituiscono risultati migliori.

Il primo forecast è ottenuto unendo “naive trend method” + “naive seasonality method” + “arima(4,0,6) remainder

#head(df)
y_rem_pred_for_mean <- as.vector(y_rem_pred_for_mean)
sales_log_naive_point_pred <- y_trend_naive_pred + y_seas_naive_pred + y_rem_pred_for_mean
sales_log_naive_point_for <- c(sales_log[c(1:TrnS_length)], sales_log_naive_point_pred)
y_log_naive_pred_RH_for_080_low_int <- y_trend_naive_pred + y_seas_naive_pred + y_res_log_pred_RH_for_080_low_int
y_log_naive_pred_RH_for_080_upp_int <- y_trend_naive_pred + y_seas_naive_pred + y_res_log_pred_RH_for_080_upp_int
y_log_naive_pred_RH_for_095_low_int <- y_trend_naive_pred + y_seas_naive_pred + y_res_log_pred_RH_for_095_low_int
y_log_naive_pred_RH_for_095_upp_int <- y_trend_naive_pred + y_seas_naive_pred + y_res_log_pred_RH_for_095_upp_int

sales_log_boot_080_low_naive_for_int <- c(rep(NA,TrnS_length),y_log_naive_pred_RH_for_080_low_int)
sales_log_boot_080_upp_naive_for_int <- c(rep(NA,TrnS_length),y_log_naive_pred_RH_for_080_upp_int)
sales_log_boot_095_low_naive_for_int <- c(rep(NA,TrnS_length),y_log_naive_pred_RH_for_095_low_int)
sales_log_boot_095_upp_naive_for_int <- c(rep(NA,TrnS_length),y_log_naive_pred_RH_for_095_upp_int)

sales_naive_pred_df <- add_column(df, sales_log=sales_log, 
                                sales_log_naive_point_for=sales_log_naive_point_for,
                                sales_log_boot_080_low_naive_for_int=sales_log_boot_080_low_naive_for_int, 
                                sales_log_boot_080_upp_naive_for_int=sales_log_boot_080_upp_naive_for_int,
                                sales_log_boot_095_low_naive_for_int=sales_log_boot_095_low_naive_for_int, 
                                sales_log_boot_095_upp_naive_for_int=sales_log_boot_095_upp_naive_for_int,
                                .after="sales")
tail(sales_naive_pred_df, 20)

Viene inizialmente effettuato un plot in cui in cui viene messo a confronto il path reale dei dati con quello predetto utilizzando solo le medie delle predizioni.

autoplot(ts(log(df_Camry_US_sales$sales), start=c(2009, 01), end=c(2022, 11), frequency = 12), serie ="Toyota Camry path") +
  autolayer(ts(sales_log_naive_point_pred, start=c(2021, 12), end=c(2022, 11), frequency = 12), series="Forecast with naive trend") +
  xlab("Month") + ylab("Sales") +
  ggtitle("Forecast Toyota Camry sales") +
  guides(colour=guide_legend(title="Legend"))

Per poi passare a un plot più completo in cui sono presenti gli intervalli di predizione dati dallo studio dei residui del modello ARMA.

Data_df <- sales_naive_pred_df
length <- nrow(Data_df)
T <- TrnS_length
title_content <- bquote(atop("Line Plot of the Toyota Camry sales Training Set and Predicted Test Sets - from ", .(First_Date), " to ", .(Last_Date)))
subtitle_content <- bquote(paste("Training set length ", .(TrnS_length), " sample points. Test set length ", .(TstS_length), " sample points."))
caption_content <- ("Author: Matteo Chiacchia")
x_name <- bquote("")
# library(numbers)
# primeFactors(T)
x_breaks_num <- 33
x_breaks_min <- Data_df$t[1]
x_breaks_max <- Data_df$t[length]
x_binwidth <- floor((x_breaks_max-x_breaks_min)/x_breaks_num)
x_breaks <- seq(from=x_breaks_min, to=x_breaks_max, by=x_binwidth)
if((x_breaks_max-max(x_breaks))>x_binwidth/2){x_breaks <- c(x_breaks,x_breaks_max)}
x_labs <- paste(Data_df$Month[x_breaks],Data_df$Year[x_breaks])
J <- 0
x_lims <- c(x_breaks_min-J*x_binwidth, x_breaks_max+J*x_binwidth)
y_name <- bquote("Sales")
y_breaks_num <- 10
y_max <- max(na.omit(Data_df$sales_log),na.omit(Data_df$sales_log_boot_095_upp_naive_for_int))
y_min <- min(na.omit(Data_df$sales_log),na.omit(Data_df$sales_log_boot_095_low_naive_for_int))
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- floor(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
line_black   <- bquote("in-sample path")
line_magenta <- bquote("real path")
line_brown   <- bquote("predicted path")
line_green   <- bquote("80% pred.int.")
# line_blue    <- bquote("95% pred.int.")
line_red     <- bquote("95% pred.int.")
leg_line_labs   <- c(line_black, line_brown, line_magenta, line_green, line_red)
leg_line_breaks <- c("line_black", "line_brown", "line_magenta", "line_green", "line_red")
leg_line_cols   <- c("line_black"="black", "line_brown"="brown", "line_magenta"="magenta",
                     "line_green"="green", "line_red"="red")
# leg_line_labs   <- c(line_black, line_brown, line_magenta, line_green, line_blue, line_red)
# leg_line_breaks <- c("line_black", "line_brown", "line_magenta", "line_green", "line_blue", "line_red")
# leg_line_cols   <- c("line_black"="black", "line_brown"="brown", "line_magenta"="magenta",
#                      "line_green"="green", "line_blue"="blue", "line_red"="red")
fill_g <- bquote("90% pred. band")
# fill_b <- bquote("95% pred. band")
fill_r <- bquote("95% pred. band")
fill_g <- bquote("90% pred. band")
fill_b <- bquote("95% pred. band")
fill_r <- bquote("99% pred. band")
leg_fill_labs   <- c( fill_g, fill_r)
leg_fill_breaks <- c("fill_g", "fill_r")
leg_fill_cols   <- c("fill_g"="lightgreen", "fill_r"="orangered")
leg_fill_labs   <- c( fill_g, fill_b, fill_r)
leg_fill_breaks <- c("fill_g", "fill_b", "fill_r")
leg_fill_cols   <- c("fill_g"="lightgreen", "fill_b"="blue", "fill_r"="orangered")
leg_col_labs    <- leg_line_labs
leg_col_breaks  <- leg_line_breaks
leg_col_cols    <- leg_line_cols
y_pred_lp <- ggplot(Data_df, aes(x=t)) + 
  geom_line(data=subset(Data_df, Data_df$t <= t[T+1]), aes(y=sales_log, color="line_black"),
            linetype="solid", alpha=1, size=0.3, group=1) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log, color="line_magenta"),
            linetype="solid", alpha=1, size=0.3, group=1) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_naive_point_for , colour="line_brown"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_095_low_naive_for_int, colour="line_red"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_095_upp_naive_for_int, colour="line_red"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_080_low_naive_for_int, colour="line_green"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_080_upp_naive_for_int, colour="line_green"),
            linetype="solid", alpha=1, size=0.3) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="orangered",
              aes(ymin=sales_log_boot_095_low_naive_for_int, ymax=sales_log_boot_095_upp_naive_for_int, fill="fill_r")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="orangered",
              aes(ymin=sales_log_boot_095_low_naive_for_int, ymax=sales_log_boot_095_upp_naive_for_int, fill="fill_r")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="blue",
              aes(ymin=sales_log_boot_095_low_naive_for_int, ymax=sales_log_boot_080_low_naive_for_int, fill="fill_b")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="blue",
              aes(ymin=sales_log_boot_080_upp_naive_for_int, ymax=sales_log_boot_095_upp_naive_for_int, fill="fill_b")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="green",
              aes(ymin=sales_log_boot_080_low_naive_for_int, ymax=sales_log_boot_080_upp_naive_for_int, fill="fill_g")) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=x_labs, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  guides(linetype="none", shape="none") +
  scale_colour_manual(name="Legend", labels=leg_line_labs, values=leg_line_cols, breaks=leg_line_breaks) +
  scale_fill_manual(name="", labels=leg_fill_labs, values=leg_fill_cols, breaks=leg_fill_breaks) +
  guides(colour=guide_legend(order=1), fill=guide_legend(order=2)) +
  theme(plot.title=element_text(hjust = 0.5), 
        plot.subtitle=element_text(hjust =  0.5),
        plot.caption = element_text(hjust = 1.0),
        axis.text.x = element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width = unit(0.8,"cm"), legend.position="bottom")
plot(y_pred_lp)

Total forecast with Holt Winters method

Il secondo forecast è ottenuto unendo “Holt Winters trend method” + “naive seasonality method” + “arima(4,0,6) remainder

#head(df)
sales_log <- as.vector(log(df$sales))
sales_log_test <- sales_log[c((TrnS_length+1):(TrnS_length+TstS_length))]
y_rem_pred_for_mean <- as.vector(y_rem_pred_for_mean)
sales_log_HW_point_pred <- y_trend_HW_pred_mean + y_seas_naive_pred + y_rem_pred_for_mean
#show(sales_log_naive_point_pred)
sales_log_HW_point_for <- c(sales_log[c(1:TrnS_length)], sales_log_HW_point_pred)
y_log_HW_pred_RH_for_080_low_int <- y_trend_HW_pred_mean + y_seas_naive_pred  + y_res_log_pred_RH_for_080_low_int
y_log_HW_pred_RH_for_080_upp_int <- y_trend_HW_pred_mean + y_seas_naive_pred  + y_res_log_pred_RH_for_080_upp_int
y_log_HW_pred_RH_for_095_low_int <- y_trend_HW_pred_mean + y_seas_naive_pred  + y_res_log_pred_RH_for_095_low_int
y_log_HW_pred_RH_for_095_upp_int <- y_trend_HW_pred_mean + y_seas_naive_pred  + y_res_log_pred_RH_for_095_upp_int

sales_log_boot_080_low_HW_for_int <- c(rep(NA,TrnS_length),y_log_HW_pred_RH_for_080_low_int)
sales_log_boot_080_upp_HW_for_int <- c(rep(NA,TrnS_length),y_log_HW_pred_RH_for_080_upp_int)
sales_log_boot_095_low_HW_for_int <- c(rep(NA,TrnS_length),y_log_HW_pred_RH_for_095_low_int)
sales_log_boot_095_upp_HW_for_int <- c(rep(NA,TrnS_length),y_log_HW_pred_RH_for_095_upp_int)

sales_HW_pred_df <- add_column(df, sales_log=sales_log, 
                                sales_log_HW_point_for=sales_log_HW_point_for,
                                sales_log_boot_080_low_HW_for_int=sales_log_boot_080_low_HW_for_int, 
                                sales_log_boot_080_upp_HW_for_int=sales_log_boot_080_upp_HW_for_int,
                                sales_log_boot_095_low_HW_for_int=sales_log_boot_095_low_HW_for_int, 
                                sales_log_boot_095_upp_HW_for_int=sales_log_boot_095_upp_HW_for_int,
                                .after="sales")

Viene inizialmente effettuato un plot in cui in cui viene messo a confronto il path reale dei dati con quello predetto.

autoplot(ts(log(df_Camry_US_sales$sales), start=c(2009, 01), end=c(2022, 11), frequency = 12), series= "Toyota Camry path") +
  autolayer(ts(sales_log_HW_point_pred, start=c(2021, 12), end=c(2022, 11), frequency = 12), series="Forecast with HW trend") +
  xlab("Month") + ylab("Sales") +
  ggtitle("Forecast Toyota Camry sales") +
  guides(colour=guide_legend(title="Legend"))

Per poi passare a un plot più completo in cui sono presenti gli intervalli di predizione dati dallo studio dei residui del modello ARMA.

Data_df <- sales_HW_pred_df
length <- nrow(Data_df)
T <- TrnS_length
title_content <- bquote(atop("Line Plot of the Toyota Camry sales Training Set and Predicted Test Sets - from ", .(First_Date), " to ", .(Last_Date)))
subtitle_content <- bquote(paste("Training set length ", .(TrnS_length), " sample points. Test set length ", .(TstS_length), " sample points."))
caption_content <- ("Author: Matteo Chiacchia")
x_name <- bquote("")
# library(numbers)
# primeFactors(T)
x_breaks_num <- 33
x_breaks_min <- Data_df$t[1]
x_breaks_max <- Data_df$t[length]
x_binwidth <- floor((x_breaks_max-x_breaks_min)/x_breaks_num)
x_breaks <- seq(from=x_breaks_min, to=x_breaks_max, by=x_binwidth)
if((x_breaks_max-max(x_breaks))>x_binwidth/2){x_breaks <- c(x_breaks,x_breaks_max)}
x_labs <- paste(Data_df$Month[x_breaks],Data_df$Year[x_breaks])
J <- 0
x_lims <- c(x_breaks_min-J*x_binwidth, x_breaks_max+J*x_binwidth)
y_name <- bquote("Sales")
y_breaks_num <- 10
y_max <- max(na.omit(Data_df$sales_log),na.omit(Data_df$sales_log_boot_095_upp_HW_for_int))
y_min <- min(na.omit(Data_df$sales_log),na.omit(Data_df$sales_log_boot_095_low_HW_for_int))
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- floor(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
line_black   <- bquote("in-sample path")
line_magenta <- bquote("real path")
line_brown   <- bquote("predicted path")
line_green   <- bquote("80% pred.int.")
# line_blue    <- bquote("95% pred.int.")
line_red     <- bquote("95% pred.int.")
leg_line_labs   <- c(line_black, line_brown, line_magenta, line_green, line_red)
leg_line_breaks <- c("line_black", "line_brown", "line_magenta", "line_green", "line_red")
leg_line_cols   <- c("line_black"="black", "line_brown"="brown", "line_magenta"="magenta",
                     "line_green"="green", "line_red"="red")
# leg_line_labs   <- c(line_black, line_brown, line_magenta, line_green, line_blue, line_red)
# leg_line_breaks <- c("line_black", "line_brown", "line_magenta", "line_green", "line_blue", "line_red")
# leg_line_cols   <- c("line_black"="black", "line_brown"="brown", "line_magenta"="magenta",
#                      "line_green"="green", "line_blue"="blue", "line_red"="red")
fill_g <- bquote("90% pred. band")
# fill_b <- bquote("95% pred. band")
fill_r <- bquote("95% pred. band")
fill_g <- bquote("90% pred. band")
fill_b <- bquote("95% pred. band")
fill_r <- bquote("99% pred. band")
leg_fill_labs   <- c( fill_g, fill_r)
leg_fill_breaks <- c("fill_g", "fill_r")
leg_fill_cols   <- c("fill_g"="lightgreen", "fill_r"="orangered")
leg_fill_labs   <- c( fill_g, fill_b, fill_r)
leg_fill_breaks <- c("fill_g", "fill_b", "fill_r")
leg_fill_cols   <- c("fill_g"="lightgreen", "fill_b"="blue", "fill_r"="orangered")
leg_col_labs    <- leg_line_labs
leg_col_breaks  <- leg_line_breaks
leg_col_cols    <- leg_line_cols
y_pred_lp <- ggplot(Data_df, aes(x=t)) + 
  geom_line(data=subset(Data_df, Data_df$t <= t[T+1]), aes(y=sales_log, color="line_black"),
            linetype="solid", alpha=1, size=0.3, group=1) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log, color="line_magenta"),
            linetype="solid", alpha=1, size=0.3, group=1) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_HW_point_for , colour="line_brown"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_095_low_HW_for_int, colour="line_red"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_095_upp_HW_for_int, colour="line_red"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_080_low_HW_for_int, colour="line_green"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_080_upp_HW_for_int, colour="line_green"),
            linetype="solid", alpha=1, size=0.3) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="orangered",
              aes(ymin=sales_log_boot_095_low_HW_for_int, ymax=sales_log_boot_095_upp_HW_for_int, fill="fill_r")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="orangered",
              aes(ymin=sales_log_boot_095_low_HW_for_int, ymax=sales_log_boot_095_upp_HW_for_int, fill="fill_r")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="blue",
              aes(ymin=sales_log_boot_095_low_HW_for_int, ymax=sales_log_boot_080_low_HW_for_int, fill="fill_b")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="blue",
              aes(ymin=sales_log_boot_080_upp_HW_for_int, ymax=sales_log_boot_095_upp_HW_for_int, fill="fill_b")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="green",
              aes(ymin=sales_log_boot_080_low_HW_for_int, ymax=sales_log_boot_080_upp_HW_for_int, fill="fill_g")) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=x_labs, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  guides(linetype="none", shape="none") +
  scale_colour_manual(name="Legend", labels=leg_line_labs, values=leg_line_cols, breaks=leg_line_breaks) +
  scale_fill_manual(name="", labels=leg_fill_labs, values=leg_fill_cols, breaks=leg_fill_breaks) +
  guides(colour=guide_legend(order=1), fill=guide_legend(order=2)) +
  theme(plot.title=element_text(hjust = 0.5), 
        plot.subtitle=element_text(hjust =  0.5),
        plot.caption = element_text(hjust = 1.0),
        axis.text.x = element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width = unit(0.8,"cm"), legend.position="bottom")
plot(y_pred_lp)

Total forecast with Gran Mean method

Il terzo forecast è ottenuto unendo “Gran Mean trend method” + “naive seasonality method” + “arima(4,0,6) remainder

#head(df)
sales_log <- as.vector(log(df$sales))
sales_log_test <- sales_log[c((TrnS_length+1):(TrnS_length+TstS_length))]
y_rem_pred_for_mean <- as.vector(y_rem_pred_for_mean)
sales_log_gran_mean_point_pred <- y_gran_mean_pred + y_seas_naive_pred + y_rem_pred_for_mean

sales_log_gran_mean_point_for <- c(sales_log[c(1:TrnS_length)], sales_log_gran_mean_point_pred)
y_log_gran_mean_pred_RH_for_080_low_int <- y_gran_mean_pred + y_seas_naive_pred  + y_res_log_pred_RH_for_080_low_int
y_log_gran_mean_pred_RH_for_080_upp_int <- y_gran_mean_pred + y_seas_naive_pred  + y_res_log_pred_RH_for_080_upp_int
y_log_gran_mean_pred_RH_for_095_low_int <- y_gran_mean_pred + y_seas_naive_pred  + y_res_log_pred_RH_for_095_low_int
y_log_gran_mean_pred_RH_for_095_upp_int <- y_gran_mean_pred + y_seas_naive_pred  + y_res_log_pred_RH_for_095_upp_int

sales_log_boot_080_low_gran_mean_for_int <- c(rep(NA,TrnS_length),y_log_gran_mean_pred_RH_for_080_low_int)
sales_log_boot_080_upp_gran_mean_for_int <- c(rep(NA,TrnS_length),y_log_gran_mean_pred_RH_for_080_upp_int)
sales_log_boot_095_low_gran_mean_for_int <- c(rep(NA,TrnS_length),y_log_gran_mean_pred_RH_for_095_low_int)
sales_log_boot_095_upp_gran_mean_for_int <- c(rep(NA,TrnS_length),y_log_gran_mean_pred_RH_for_095_upp_int)

sales_gran_mean_pred_df <- add_column(df, sales_log=sales_log, 
                                sales_log_gran_mean_point_for=sales_log_gran_mean_point_for,
                                sales_log_boot_080_low_gran_mean_for_int=sales_log_boot_080_low_gran_mean_for_int, 
                                sales_log_boot_080_upp_gran_mean_for_int=sales_log_boot_080_upp_gran_mean_for_int,
                                sales_log_boot_095_low_gran_mean_for_int=sales_log_boot_095_low_gran_mean_for_int, 
                                sales_log_boot_095_upp_gran_mean_for_int=sales_log_boot_095_upp_gran_mean_for_int,
                                .after="sales")

Viene inizialmente effettuato un plot in cui in cui viene messo a confronto il path reale dei dati con quello predetto.

autoplot(ts(log(df_Camry_US_sales$sales), start=c(2009, 01), end=c(2022, 11), frequency = 12), series = "Toyota Camry real path") +
  autolayer(ts(sales_log_gran_mean_point_pred, start=c(2021, 12), end=c(2022, 11), frequency = 12), series="Forecast with Gran-Mean trend") +
  xlab("Month") + ylab("Sales") +
  ggtitle("Forecast Toyota Camry sales") +
  guides(colour=guide_legend(title="Legend"))

Per poi passare a un plot più completo in cui sono presenti gli intervalli di predizione dati dallo studio dei residui del modello ARMA.

Data_df <- sales_gran_mean_pred_df
length <- nrow(Data_df)
T <- TrnS_length
title_content <- bquote(atop("Line Plot of the Toyota Camry sales Training Set and Predicted Test Sets - from ", .(First_Date), " to ", .(Last_Date)))
subtitle_content <- bquote(paste("Training set length ", .(TrnS_length), " sample points. Test set length ", .(TstS_length), " sample points."))
caption_content <- ("Author: Matteo Chiacchia")
x_name <- bquote("")
# library(numbers)
# primeFactors(T)
x_breaks_num <- 33
x_breaks_min <- Data_df$t[1]
x_breaks_max <- Data_df$t[length]
x_binwidth <- floor((x_breaks_max-x_breaks_min)/x_breaks_num)
x_breaks <- seq(from=x_breaks_min, to=x_breaks_max, by=x_binwidth)
if((x_breaks_max-max(x_breaks))>x_binwidth/2){x_breaks <- c(x_breaks,x_breaks_max)}
x_labs <- paste(Data_df$Month[x_breaks],Data_df$Year[x_breaks])
J <- 0
x_lims <- c(x_breaks_min-J*x_binwidth, x_breaks_max+J*x_binwidth)
y_name <- bquote("Sales")
y_breaks_num <- 10
y_max <- max(na.omit(Data_df$sales_log),na.omit(Data_df$sales_log_boot_095_upp_gran_mean_for_int))
y_min <- min(na.omit(Data_df$sales_log),na.omit(Data_df$sales_log_boot_095_low_gran_mean_for_int))
y_binwidth <- round((y_max-y_min)/y_breaks_num, digits=3)
y_breaks_low <- floor(y_min/y_binwidth)*y_binwidth
y_breaks_up <- ceiling(y_max/y_binwidth)*y_binwidth
y_breaks <- round(seq(from=y_breaks_low, to=y_breaks_up, by=y_binwidth),digits=3)
y_labs <- format(y_breaks, scientific=FALSE)
K <- 0
y_lims <- c((y_breaks_low-K*y_binwidth), (y_breaks_up+K*y_binwidth))
line_black   <- bquote("in-sample path")
line_magenta <- bquote("real path")
line_brown   <- bquote("predicted path")
line_green   <- bquote("80% pred.int.")
# line_blue    <- bquote("95% pred.int.")
line_red     <- bquote("95% pred.int.")
leg_line_labs   <- c(line_black, line_brown, line_magenta, line_green, line_red)
leg_line_breaks <- c("line_black", "line_brown", "line_magenta", "line_green", "line_red")
leg_line_cols   <- c("line_black"="black", "line_brown"="brown", "line_magenta"="magenta",
                     "line_green"="green", "line_red"="red")
# leg_line_labs   <- c(line_black, line_brown, line_magenta, line_green, line_blue, line_red)
# leg_line_breaks <- c("line_black", "line_brown", "line_magenta", "line_green", "line_blue", "line_red")
# leg_line_cols   <- c("line_black"="black", "line_brown"="brown", "line_magenta"="magenta",
#                      "line_green"="green", "line_blue"="blue", "line_red"="red")
fill_g <- bquote("90% pred. band")
# fill_b <- bquote("95% pred. band")
fill_r <- bquote("95% pred. band")
fill_g <- bquote("90% pred. band")
fill_b <- bquote("95% pred. band")
fill_r <- bquote("99% pred. band")
leg_fill_labs   <- c( fill_g, fill_r)
leg_fill_breaks <- c("fill_g", "fill_r")
leg_fill_cols   <- c("fill_g"="lightgreen", "fill_r"="orangered")
leg_fill_labs   <- c( fill_g, fill_b, fill_r)
leg_fill_breaks <- c("fill_g", "fill_b", "fill_r")
leg_fill_cols   <- c("fill_g"="lightgreen", "fill_b"="blue", "fill_r"="orangered")
leg_col_labs    <- leg_line_labs
leg_col_breaks  <- leg_line_breaks
leg_col_cols    <- leg_line_cols
y_pred_lp <- ggplot(Data_df, aes(x=t)) + 
  geom_line(data=subset(Data_df, Data_df$t <= t[T+1]), aes(y=sales_log, color="line_black"),
            linetype="solid", alpha=1, size=0.3, group=1) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log, color="line_magenta"),
            linetype="solid", alpha=1, size=0.3, group=1) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_gran_mean_point_for , colour="line_brown"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_095_low_gran_mean_for_int, colour="line_red"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_095_upp_gran_mean_for_int, colour="line_red"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_080_low_gran_mean_for_int, colour="line_green"),
            linetype="solid", alpha=1, size=0.3) +
  geom_line(data=subset(Data_df, Data_df$t >= t[T+1]), aes(y=sales_log_boot_080_upp_gran_mean_for_int, colour="line_green"),
            linetype="solid", alpha=1, size=0.3) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="orangered",
              aes(ymin=sales_log_boot_095_low_gran_mean_for_int, ymax=sales_log_boot_095_upp_gran_mean_for_int, fill="fill_r")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="orangered",
              aes(ymin=sales_log_boot_095_low_gran_mean_for_int, ymax=sales_log_boot_095_upp_gran_mean_for_int, fill="fill_r")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="blue",
              aes(ymin=sales_log_boot_095_low_gran_mean_for_int, ymax=sales_log_boot_080_low_gran_mean_for_int, fill="fill_b")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="blue",
              aes(ymin=sales_log_boot_080_upp_gran_mean_for_int, ymax=sales_log_boot_095_upp_gran_mean_for_int, fill="fill_b")) +
  geom_ribbon(data=subset(Data_df, Data_df$t >= t[T+1]), alpha=0.3, colour="green",
              aes(ymin=sales_log_boot_080_low_gran_mean_for_int, ymax=sales_log_boot_080_upp_gran_mean_for_int, fill="fill_g")) +
  scale_x_continuous(name=x_name, breaks=x_breaks, labels=x_labs, limits=x_lims) +
  scale_y_continuous(name=y_name, breaks=y_breaks, labels=NULL, limits=y_lims,
                     sec.axis= sec_axis(~., breaks=y_breaks, labels=y_labs)) +
  ggtitle(title_content) +
  labs(subtitle=subtitle_content, caption=caption_content) +
  guides(linetype="none", shape="none") +
  scale_colour_manual(name="Legend", labels=leg_line_labs, values=leg_line_cols, breaks=leg_line_breaks) +
  scale_fill_manual(name="", labels=leg_fill_labs, values=leg_fill_cols, breaks=leg_fill_breaks) +
  guides(colour=guide_legend(order=1), fill=guide_legend(order=2)) +
  theme(plot.title=element_text(hjust = 0.5), 
        plot.subtitle=element_text(hjust =  0.5),
        plot.caption = element_text(hjust = 1.0),
        axis.text.x = element_text(angle=-45, vjust=1, hjust=-0.3),
        legend.key.width = unit(0.8,"cm"), legend.position="bottom")
plot(y_pred_lp)

3. Accuracy

Una volta utilizzati i diversi metodi di forecast si passa all’analisi dei risultati.

autoplot(ts(log(df_Camry_US_sales$sales), start=c(2009, 01), end=c(2022, 11), frequency = 12), series = "Real Path") +
  autolayer(ts(sales_log_gran_mean_point_pred, start=c(2021, 12), end=c(2022, 11), frequency = 12), series="GranMean")+
  autolayer(ts(sales_log_naive_point_pred, start=c(2021, 12), end=c(2022, 11), frequency = 12), series="Naive")+
  autolayer(ts(sales_log_HW_point_pred, start=c(2021, 12), end=c(2022, 11), frequency = 12), series="HW")+
  xlab("Month") + ylab("Sales") +
  ggtitle("Forecasts Toyota Camry sales") +
  guides(colour=guide_legend(title="Forecast"))

Mettendo a confronto i lineplot delle predizioni si nota sostanzialmente che il metodo con la gran mean tende a sovrastimare i valori di conseguenza sembra essere il peggiore. Per quanto riguarda Naive e HW si nota che quest ultimo, che inizialmente sembra avere un path molto vicino a quello reale, poi comincia a sottostimare fortemente i valori. Naive invece sembra avere un miglior compromesso di stima.

Per valutare numericamente la bontà delle previsione verrano utilizzate le seguenti statistiche di accuratezza, che verranno poi messe a confronto:

  • ME (Mean Error): rappresenta la media degli errori del modello. \(MAE = (1/N) \sum_{i=1}^{N} \overline{y}_t - y_t\)

  • RMSE (Root Mean Squared Error): rappresenta la radice quadrata della media dei quadrati degli errori del modello. In altre parole, può essere considerato come una sorta di distanza (normalizzata) tra il vettore dei valori previsti e il vettore dei valori osservati. \(RMSE= \sqrt{(1/N)\sum_{i=1}^{N}(\overline{y}_t - y_t)^2}\)

  • MAE (Mean Absolute Error): rappresenta la media degli errori assoluti del modello. È una metrica semplice e facilmente interpretabile, ma può essere influenzata da valori estremi (outliers) nella distribuzione dei dati. \(MAE = (1/N) \sum_{i=1}^{N} |\overline{y}_t - y_t|\)

  • MPE (Mean Percentage Error): rappresenta la media percentuale degli errori del modello. Poiché nella formula sono utilizzati i valori effettivi invece dei valori assoluti degli errori di previsione, gli errori di previsione positivi e negativi possono compensarsi a vicenda; di conseguenza, la formula può essere utilizzata come misura del bias nelle previsioni. \(MPE= (1/N)(\sum_{i=1}^{N}(y_t-\overline{y}_t)/y_t) *100\)

  • MAPE (Mean Absolute Percentage Error): rappresenta la media percentuale degli errori assoluti del modello. In altre parole, è la media dei valori assoluti delle differenze percentuali tra le previsioni del modello e i valori effettivi. \(MAPE = (1/N)(\sum_{i=1}^{N}|y_t-\overline{y}_t|/y_t)*100\)

  • MASE

La statistica MASE (Mean Absolute Scaled Error) è una metrica di accuratezza utilizzata per valutare la bontà di un modello di previsione rispetto a una previsione naive. La previsione naive è un semplice modello di previsione che si basa sulla ripetizione del valore dell’ultima osservazione come previsione per le future osservazioni.

La MASE è definita come il rapporto tra l’errore medio assoluto del modello e l’errore medio assoluto del modello di previsione naive In formula:

\(MASE = (MAE_(model)) / (MAE_(naive))\)

La MASE è una metrica utile perché è indipendente dalle unità di misura della variabile che si sta prevedendo. Inoltre, la MASE è una metrica relativa, ovvero tiene conto dell’errore di previsione del modello rispetto a una previsione naive, che rappresenta un modello di base semplice ma comunque valido.

In generale, un valore di MASE inferiore a 1 indica che il modello di previsione è migliore della previsione naive, mentre un valore di MASE superiore a 1 indica che il modello di previsione è peggiore della previsione naive La MASE può essere utilizzata per confrontare la bontà di diversi modelli di previsione, o per valutare le performance di un modello di previsione rispetto a una previsione naive di riferimento.

N.B: la funzione “accuracy” della libreria forecast effettua il calcolo utilizzando \([y_t-\overline{y}_t]\) invece di \([\overline{y}_t - y_t]\)

Accuracy del forecast con “stl” function

Camry_BTC_log_stl_point_pred_accuracy <- forecast::accuracy(sales_log_stl_for[["mean"]], sales_log_test) 
show(Camry_BTC_log_stl_point_pred_accuracy)
                ME     RMSE       MAE      MPE     MAPE
Test set 0.1193813 0.206823 0.1730324 1.161795 1.713021
Camry_BTC_log_stl_point_resid <- sales_log_test-sales_log_stl_for[["mean"]]
Camry_log_stl_point_pred_MASE <- fabletools::MASE(Camry_BTC_log_stl_point_resid, sales_log,
                                                      demean = FALSE, na.rm = TRUE, .period=12)
MASE_string <- sprintf("MASE = %f\n", Camry_log_stl_point_pred_MASE)
cat(MASE_string)
MASE = 0.895172

Accuracy del forecast con naive method

Camry_BTC_log_naive_point_pred_accuracy <- forecast::accuracy(sales_log_naive_point_pred, sales_log_test) 
show(Camry_BTC_log_naive_point_pred_accuracy)
                  ME      RMSE       MAE        MPE     MAPE
Test set -0.07400812 0.1935752 0.1302366 -0.7609418 1.308794
Camry_BTC_log_naive_point_resid <- sales_log_test-sales_log_naive_point_pred
Camry_log_naive_point_pred_MASE <- fabletools::MASE(Camry_BTC_log_naive_point_resid, sales_log,
                                                      demean = FALSE, na.rm = TRUE, .period=12)
MASE_string <- sprintf("MASE = %f\n", Camry_log_naive_point_pred_MASE)
cat(MASE_string)
MASE = 0.673770

Accuracy del forecast con HW method

Camry_BTC_log_HW_point_pred_accuracy <- forecast::accuracy(sales_log_HW_point_pred, sales_log_test) 
show(Camry_BTC_log_HW_point_pred_accuracy)
                  ME      RMSE       MAE          MPE     MAPE
Test set 0.002240479 0.2090771 0.1523637 -0.008885843 1.522271
Camry_BTC_log_HW_point_resid <- sales_log_test-sales_log_HW_point_pred
Camry_log_HW_point_pred_MASE <- fabletools::MASE(Camry_BTC_log_HW_point_resid, sales_log,
                                                      demean = FALSE, na.rm = TRUE, .period=12)
MASE_string <- sprintf("MASE = %f\n", Camry_log_HW_point_pred_MASE)
cat(MASE_string)
MASE = 0.788243

Accuracy del forecast con Gran mean method

Camry_BTC_log_gran_mean_point_pred_accuracy <- forecast::accuracy(sales_log_gran_mean_point_pred, sales_log_test) 
show(Camry_BTC_log_gran_mean_point_pred_accuracy)
                 ME      RMSE       MAE       MPE     MAPE
Test set -0.2470487 0.3050036 0.2552855 -2.478649 2.558624
Camry_BTC_log_gran_mean_point_resid <- sales_log_test-sales_log_gran_mean_point_pred
Camry_log_gran_mean_point_pred_MASE <- fabletools::MASE(Camry_BTC_log_gran_mean_point_resid, sales_log,
                                                      demean = FALSE, na.rm = TRUE, .period=12)
MASE_string <- sprintf("MASE = %f\n", Camry_log_gran_mean_point_pred_MASE)
cat(MASE_string)
MASE = 1.320702

Analisi dei valori di accuracy

Dai valori ottenuti si possono effettuare vari confronti:

  • I valori della ME sono minori di 0 nei casi di naive e gran mean e maggiore di 0 nel caso di stl function e HW. Questo sta a indicare che i primi due tendono a sovrastimare i valori, mentre l’ultimo tende a sottostimarli. Essendo il valore di HW il più basso in valore assoluto indica che in media questa predizione ha il miglior compromesso tra sovrastima e sottostima.
  • Se si considera la RMSE il valore più basso è quello dato dall’utilizzo del metodo naive, indicando un miglior precisione di predizione rispetto agli altri modelli (anche se i valori differiscono di poco eccetto gran mean).
  • Il valore della MAE è nuovamente minore nel caso del modello con naive stando a indicare nuovamente una miglior precisione.
  • Per quanto riguarda la MPE il valore di HW è “il più vicino” a 0.
  • La MAPE è anch’essa minore nel caso naive.
  • Infine i valori della MASE (minori di 1) indicano che, utilizzando per il trend i metodi naive e HW, oppure la funzione di forecast automatico stl si ottengono predizioni migliori rispetto al modello di predizione banale (naive) per tutta la Time Series. Nel caso della Gran Mean si ottiene una MASE>1 indicando errori di predizione peggiori di quelli della predizione banale.

Come conclusione generale si può dire che il modello che utilizza il naive method per il forecast del trend è il più accurato.

Infine si salvano gli errori derivati sia dal fitting dei dati del Trainset sia dalla prediction dei dati del Testset per poter comparare, tramite il test di Diebold-Mariano, il modello attuale con quello che si proverà a utilizzare in seguito.

write.csv(Camry_TrnS_df, "Camry_US_sales_trainset.csv")
sales_log_naive_point_pred_df = as.data.frame(sales_log_naive_point_pred)
Camry_BTC_log_naive_point_resid_df = as.data.frame(Camry_BTC_log_naive_point_resid)
write.csv(sales_log_naive_point_pred_df, "sales_log_stl_point_pred_df")
write.csv(Camry_BTC_log_naive_point_resid_df, "Camry_BTC_log_stl_point_resid_df.csv")
LS0tCnRpdGxlOiAiVG95b3RhIENhbXJ5IHNhbGVzIFRpbWUgU2VyaWVzIGFuYWx5c2lzIGFuZCBmb3JlY2FzdCB1c2luZyBTVEwgbW9kZWwiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQojIyMjIyMgQXV0aG9yOiBNYXR0ZW8gQ2hpYWNjaGlhCiMjIyMjIyBQcm9mLiBSb2JlcnRvIE1vbnRlCgpM4oCZb2JpZXR0aXZvIGRlbGxvIHN0dWRpbyDDqCBlZmZldHR1YXJlIGlsICpmb3JlY2FzdCogZGVsbGEgc2VyaWUgc3RvcmljYSByZWxhdGl2YSBhbGxlIHZlbmRpdGUgbWVuc2lsaSBkZWxsYSAqVG95b3RhIENhbXJ5KiBuZWdsaSBVU0EuClBlciByYWdnaXVuZ2VyZSBsJ29iaWV0dGl2byBkZXNpZGVyYXRvLCBzb25vIHN0YXRpIHV0aWxpenphdGUgZGVsbGUgdGVjbmljaGUgY2hlLCBhIHBhcnRpcmUgZGFsbCdhbmFsaXNpIGFjY3VyYXRhIGRlaSBkYXRpIHBhc3NhdGksIHNvbm8gaW4gZ3JhZG8gZGkgY3JlYXJlIHVuIG1vZGVsbG8gZGkgcHJldmlzaW9uZSBjaGUgc2lhIHBpw7kgbyBtZW5vIGFjY3VyYXRvLiBJbiBwYXJ0aWNvbGFyZSBzb25vIHN0YXRpIHV0aWxpenphdGk6CgotICpNb2RlbGxvKiAqKipTVEwqKio6IFNlYXNvbmFsIGFuZCBUcmVuZCBkZWNvbXBvc2l0aW9uIHVzaW5nIExPRVNTLgotICpNb2RlbGxvKiAqKipFVFMqKio6IEVycm9yLVRyZW5kLVNlYXNvbmFsaXR5IAoKIyMgTW9kZWxsbyBTVEwKCkwnYWxnb3JpdG1vIFNUTCAoU2Vhc29uYWwgYW5kIFRyZW5kIGRlY29tcG9zaXRpb24gdXNpbmcgTG9lc3MpIHV0aWxpenphIGxhIHRlY25pY2EgZGkgc21vb3RoaW5nIExPRVNTIHBlciBhbmFsaXp6YXJlIHVuYSBzZXJpZSB0ZW1wb3JhbGUuIEVzc28gZXNlZ3VlIGR1ZSBjaWNsaSBkaSAqKnNtb290aGluZyoqOiB1bm8gaW50ZXJubyBjaGUgaXRlcmEgdHJhIGxvICpzbW9vdGhpbmcqIHN0YWdpb25hbGUgZSBxdWVsbG8gZGkgdHJlbmQgZSB1bm8gZXN0ZXJubyBjaGUgcmlkdWNlIGFsIG1pbmltbyBsJ2VmZmV0dG8gZGVnbGkgKm91dGxpZXIqIGR1cmFudGUgaWwgcHJvY2Vzc28gZGkgc21vb3RoaW5nIGludGVybm8uIApOZWwgcHJpbW8gY2ljbG8sIHZpZW5lIGNhbGNvbGF0YSBsYSBjb21wb25lbnRlICpzdGFnaW9uYWxlKiBlIHJpbW9zc2EgcGVyIGNhbGNvbGFyZSBsYSBjb21wb25lbnRlIGRpICp0cmVuZC4qIE5lbCBzZWNvbmRvIGNpY2xvLCBsZSBjb21wb25lbnRpIHN0YWdpb25hbGkgZSBkaSB0cmVuZCB2ZW5nb25vIHNvdHRyYXR0ZSBkYWxsYSBzZXJpZSB0ZW1wb3JhbGUgcGVyIG90dGVuZXJlIGlsICpyZW1haW5kZXIqLiBJbiBxdWVzdG8gbW9kbywgbCdhbGdvcml0bW8gKlNUTCogc3VkZGl2aWRlIGxhIHNlcmllIHRlbXBvcmFsZSBpbiB0cmUgY29tcG9uZW50aSBwcmluY2lwYWxpOiBsYSBjb21wb25lbnRlICpzdGFnaW9uYWxlKiwgbGEgY29tcG9uZW50ZSBkaSAqdHJlbmQqIGUgaWwgKnJlbWFpbmRlciosIGNoZSBwdcOyIGVzc2VyZSBpbnRlcnByZXRhdG8gY29tZSBpbCAqbm9pc2UqIHJlc2lkdW8uCsOIIHBvc3NpYmlsZSBhcHBsaWNhcmUgbGEgZGVjb21wb3NpemlvbmUgKipTVEwqKiBhIHF1YWxzaWFzaSBzZXQgZGkgZGF0aSwgbWEgcmlzdWx0YXRpIHNpZ25pZmljYXRpdmkgdmVuZ29ubyBvdHRlbnV0aSBzb2xvIHNlIG5laSBkYXRpIGVzaXN0ZSB1bmEgY29tcG9uZW50ZSBzdGFnaW9uYWxlLgoKUmlzcGV0dG8gYWQgYWx0cmUgbWV0b2RvbG9naWU6CgotIFB1w7IgY29uc2lkZXJhcmUgb2duaSB0aXBvIGRpIHN0YWdpb25hbGl0w6AKLSBMYSBjb21wb25lbnRlIHN0YWdpb25hbGUgcHXDsiB2YXJpYXJlIG5lbCB0ZW1wbyBlZCDDqCB1biBwYXJhbWV0cm8gc2V0dGFiaWxlIGRhbGzigJl1dGVudGUsIGNvc8OsIGNvbWUgbG8gc21vb3RoaW5nIGRpIHRyZW5kIGUgY2ljbGljaXTDoAotIFByZXNlbnRhIHVuIHRyYXR0YW1lbnRvIGRlZ2xpIG91dGxpZXJzIGVzdHJlbWFtZW50ZSByb2J1c3RvLCBjaGUgcGVyw7IgaW5mbHVlbnphIGkgcmVzaWR1aS4KCiMjIFRpbWUgc2VyaWVzIGFuYWx5c2lzIGFuZCBmb3JlY2FzdCB3aXRoIFNUTAoKTG8gc3ZvbGdpbWVudG8gZGVsbG8gc3R1ZGlvIGVmZmV0dHVhdG8gw6ggY2xhc3NpZmljYWJpbGUgaW4gZHVlIG1hY3JvIGNhdGVnb3JpZToKCi0gKioqQW5hbGlzaSoqKjogc2NvbXBvc2l6aW9uZSBkZWxsYSBzZXJpZSBzdG9yaWNhIG5lbGxlIHN1ZSBjb21wb25lbnRpICgqVHJlbmQqLCpTZWFzb25hbGl0eSopLiBVdGlsaXp6byBkZWwgbW9kZWxsbyAqKkFSTUEqKiAoQXV0b3JlZ3Jlc3NpdmXigJNtb3ZpbmctYXZlcmFnZSBtb2RlbCkgcGVyIGxhIGRlc2NyaXppb25lIGRlbGxhIHBhcnRlIHN0YXppb25hcmlhICgqUmVtYWluZGVyKikuCgotICoqKlByZXZpc2lvbmUqKio6IHVuaW9uZSBhZGRpdGl2YSBkZWxsZSBwcmVkaXppb25pIGVmZmV0dHVhdGUgc3VsbGUgY29tcG9uZW50aSBpbiBtYW5pZXJhIGlzb2xhdGEuCgojIyMgMS4gQW5hbGlzaQoKSWwgcHJpbW8gcGFzc2FnZ2lvIMOoIGNhcmljYXJlIGxlIGxpYnJlcmllIGRpIHJpZmVyaW1lbnRvIApgYGB7cn0KbGlicmFyeShiYXNlKQpsaWJyYXJ5KHN0YXRzKQpsaWJyYXJ5KGFzdHNhKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShkcGx5cikKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkobnVtYmVycykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KEVudlN0YXRzKQpsaWJyYXJ5KERlc2NUb29scykKbGlicmFyeShsYXR0aWNlKQpsaWJyYXJ5KGxlYXBzKQpsaWJyYXJ5KGx0c2EpCmxpYnJhcnkoYmVzdGdsbSkKbGlicmFyeSh6b28pCmxpYnJhcnkobG10ZXN0KQpsaWJyYXJ5KGZvcmVjYXN0KQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShncmlkKQpsaWJyYXJ5KGd0YWJsZSkKbGlicmFyeSh0c2liYmxlKQpsaWJyYXJ5KGZhYmxldG9vbHMpCmxpYnJhcnkoZmFibGUpCmxpYnJhcnkoZmVhc3RzKQpsaWJyYXJ5KGNyYXlvbikKbGlicmFyeShmQmFzaWNzKQpsaWJyYXJ5KG5vcnRlc3QpCmxpYnJhcnkodHNlcmllcykKbGlicmFyeShzdXJ2aXZhbCkKbGlicmFyeShNQVNTKQpsaWJyYXJ5KGZpdGRpc3RycGx1cykKbGlicmFyeShnbG9naXMpCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KHByYWNtYSkKbGlicmFyeShObGNPcHRpbSkKbGlicmFyeShnb2Z0ZXN0KQpsaWJyYXJ5KHFxcGxvdHIpCmxpYnJhcnkoQmlvY01hbmFnZXIpCmxpYnJhcnkoc3RhdHM0KQpsaWJyYXJ5KGR5bmFtaWNUcmVlQ3V0KQpsaWJyYXJ5KGZhc3RjbHVzdGVyKQpsaWJyYXJ5KHh0cykKbGlicmFyeShUVFIpCmxpYnJhcnkocXVhbnRtb2QpCmxpYnJhcnkodXJjYSkKbGlicmFyeShmcHAzKQpgYGAKYGBge3J9CmdyYXBoaWNzLm9mZigpCiMgUmVtb3ZlcyBhbGwgaXRlbXMgaW4gRW52aXJvbm1lbnQhCnJtKGxpc3Q9bHMoKSkKIyBUbyByZXNldCBvcHRpb25zIHRvIGRlZmF1bHQgdmFsdWVzCmRlZl9vcHRpb25zIDwtIG9wdGlvbnMoKQojIFNldHMgdGhlIGRhdGEgZGlyZWN0b3J5LgpXRCA8LSBkaXJuYW1lKHJzdHVkaW9hcGk6OmdldFNvdXJjZUVkaXRvckNvbnRleHQoKSRwYXRoKQpzaG93KFdEKQpzZXR3ZChXRCkKZGlyKCkKIwojIFRvIGNsZWFyIHRoZSBjb25zb2xlCmNhdCgiXDAxNCIpCiMgRnVuY3Rpb25zCm5leHRvZGQgPC0gZnVuY3Rpb24oeCl7CiAgeCA8LSByb3VuZCh4KQogIGlmKHglJTI9PTApIHggPC0geCsxCiAgYXMuaW50ZWdlcih4KQp9CmBgYApJIGRhdGkgcmVsYXRpdmkgYWxsZSB2ZW5kaXRlIG1lbnNpbGkgZGVsbGEgKlRveW90YSBDYW1yeSogbmVnbGkgKlVTQSogc29ubyBzYWx2YXRpIGFsbCdpbnRlcm5vIGRlbCBmaWxlICpFeGNlbCogIioqQ2FtcnlfVVNfc2FsZXMueGxzeCoqIi4gVmllbmUgZWZmZXR0dWF0bywgcXVpbmRpLCBpbCBjYXJpY2FtZW50byBkZWwgZmlsZSAqRXhjZWwqIGUgaSBkYXRpIHZlbmdvbm8gc2FsdmF0aSBjb21lICpEYXRhZnJhbWUqLgpgYGB7cn0KZGZfQ2FtcnlfVVNfc2FsZXMgPC0gcmVhZF9leGNlbCgiQ2FtcnlfVVNfc2FsZXMueGxzeCIpCnRhaWwoZGZfQ2FtcnlfVVNfc2FsZXMpCmBgYApEYSBub3RhcmUgY2hlIGlsIG51bWVybyBkaSB2ZW5kaXRlIHJlbGF0aXZlIGEgKkRpY2VtYnJlIDIwMjIqIMOoIHVndWFsZSBhIDAsIGRpIGNvbnNlZ3VlbnphIHNpIMOoIGRlY2lzbyBkaSBlbGltaW5hcmUgcXVlc3RvIGRhdG8gZSBjb25zaWRlcmFyZSBjb21lIHVsdGltbyBlbGVtZW50byBkZWwgZGF0YWZyYW1lIHF1ZWxsbyByaXNhbGVudGUgYWwgbWVzZSBwcmVjZWRlbnRlICgqTm92ZW1icmUgMjAyMiopCmBgYHtyfQojZGVsZXRlIGxhc3Qgcm93IG9mIGRmIGJlY2F1c2UgaXQgaGFzIG5vdCB2YWx1ZQpkZl9DYW1yeV9VU19zYWxlcyA8LSBkZl9DYW1yeV9VU19zYWxlc1stbnJvdyhkZl9DYW1yeV9VU19zYWxlcyksIF0KdGFpbChkZl9DYW1yeV9VU19zYWxlcykKaGVhZChkZl9DYW1yeV9VU19zYWxlcykKYGBgCkkgcHJpbWkgZGF0aSByaXNhbGdvbm8gYSAqR2VubmFpbyAyMDA1KiwgbWEgc2kgw6ggZGVjaXNvIGRpIGVzZWd1aXJlIGwnYW5hbGlzaSBhIHBhcnRpcmUgZGFpIGRhdGkgZGVsICoyMDA5KiBwZXIgZXZpdGFyZSBjaGUgbCdpbmZsdWVuemEgZGVpIHZhbG9yaSBkaSBtZXJjYXRvIHRyb3BwbyBsb250YW5pIHRlbXBvcmFsbWVudGUgcG9zc2EgYXZlcmUgZWZmZXR0byBuZWdhdGl2byBkdXJhbnRlIGlsIHByb2Nlc3NvIGRpICphbmFsaXNpKiBlICpmb3JlY2FzdGluZyouCkFnZ2l1bmdpYW1vIGlub2x0cmUgdW5hIGNvbG9ubmEgKCoqdCoqKSBkaSBpbmRpY2UgYWwgKmRhdGFmcmFtZSosIGNoZSByZXBsaWNhIHNlbXBsaWNlbWVudGUgaSBudW1lcmkgZGkgcmlnYSBlZCDDqCB1dGlsZSBwZXIgc2NvcGkgZGkgdHJhY2NpYW1lbnRvLgpgYGB7cn0KZGZfQ2FtcnlfVVNfc2FsZXMkZGF0ZSA8LSBhcy5EYXRlKGRmX0NhbXJ5X1VTX3NhbGVzJGRhdGUpCiN0aW1lIHNlcmllcyBzdGFydCA9IDAxLzIwMDkKZGZfQ2FtcnlfVVNfc2FsZXMgPC0gZGZfQ2FtcnlfVVNfc2FsZXNbZGZfQ2FtcnlfVVNfc2FsZXMkZGF0ZSA+PSAiMjAwOS0wMS0zMSIsIF0KZGYgPC0gZGF0YS5mcmFtZShZZWFyPXllYXIoZGZfQ2FtcnlfVVNfc2FsZXMkZGF0ZSksIE1vbnRoPW1vbnRoKGRmX0NhbXJ5X1VTX3NhbGVzJGRhdGUpLCBkYXRlPWRmX0NhbXJ5X1VTX3NhbGVzJGRhdGUsCiAgICAgICAgICAgICAgICAgc2FsZXM9YXMudmVjdG9yKGRmX0NhbXJ5X1VTX3NhbGVzJHNhbGVzKSkKCmRmIDwtIGFkZF9jb2x1bW4oYWRkX2NvbHVtbihkZiwgdD0xOm5yb3coZGYpLCAuYmVmb3JlPSJZZWFyIikpCkRhdGFfZGYgPC0gZGYKRGF0YV9kZiA8LSBkcGx5cjo6cmVuYW1lKERhdGFfZGYsIHg9dCwgeT1zYWxlcykKaGVhZChkZikKYGBgClBlciBlZmZldHR1YXJlIGFuYWxpc2kgZSBwcmVkaXppb25lIHNpIMOoIGRlY2lzbyBkaSBzdWRkaXZpZGVyZSBpbCAqZGF0YWZyYW1lKiBpbiBkdWUgcGFydGk6ICoqKlRyYWluU2V0KioqIGUgKioqVGVzdFNldCoqKi4gCkxhIGRlY2lzaW9uZSBwcmVzYSDDqCBxdWVsbGEgZGkgc3VkZGl2aWRlcmUgaWwgKmRhdGFmcmFtZSogaW4gbW9kbyB0YWxlIGRhIGF2ZXJlIG5lbCAqKlRlc3RTZXQqKiAxMiBtZXNpIGUgcG90ZXIgcXVpbmRpIGVzZWd1aXJlIGxhIHByZWRpemlvbmUgc3UgcXVlc3RpICgqKnByZWRpemlvbmUgbWVuc2lsZSBkZWxsJ2ludGVybyBhbm5vIHN1Y2Nlc3Npdm8qKiksIHV0bGl6emFuZG8gY29tZSAqVGltZSBTZXJpZXMqIGkgdmFsb3JpIHByZXNlbnRpIG5lbCAqKlRyYWluU2V0KiogcGVyIGNyZWFyZSBpbCBtb2RlbGxvLgpgYGB7cn0KI3BhcmFtZXRlcnMKRFNfbGVuZ3RoIDwtIG5yb3coRGF0YV9kZikKVHJuU19sZW5ndGggPC0gZmxvb3IoRFNfbGVuZ3RoKjAuOTMpClRzdFNfbGVuZ3RoIDwtIERTX2xlbmd0aC1Ucm5TX2xlbmd0aApkZl9zdHJpbmcgPC0gc3ByaW50ZigiRGF0YSBGcmFtZSBsZW5naHQ6ICVkXG4iLCBEU19sZW5ndGgpCnRybl9zdHJpbmcgPC0gc3ByaW50ZigiVHJhaW4gU2V0IGxlbmdodDogJWRcbiIsIFRyblNfbGVuZ3RoKQp0c3Rfc3RyaW5nIDwtIHNwcmludGYoIlRlc3QgU2V0IGxlbmdodDogJWRcbiIsIFRzdFNfbGVuZ3RoKQpjYXQoZGZfc3RyaW5nKQpjYXQodHJuX3N0cmluZykKY2F0KHRzdF9zdHJpbmcpCmBgYApTaSB2aXN1YWxpenphbm8gaW5pemlhbG1lbnRlIGkgZGF0aSB0cmFtaXRlIGxvICoqU2NhdHRlciBQbG90KioKYGBge3J9CiMgV2UgZHJhdyB0aGUgc2NhdHRlciBwbG90LgojIFdlIHNldCB0aGUgaW5pdGlhbCBhbmQgZmluYWwgZGF0ZSBvZiB0aGUgdGltZSBzZXJpZXMuIFRoZXNlIGRhdGVzIGVudGVyIHRoZSBUaXRsZSBvZiB0aGUgcGxvdC4KRmlyc3RfRGF0ZSA8LSBwYXN0ZShEYXRhX2RmJE1vbnRoWzFdLERhdGFfZGYkWWVhclsxXSkKTGFzdF9EYXRlIDwtIHBhc3RlKERhdGFfZGYkTW9udGhbRFNfbGVuZ3RoXSxEYXRhX2RmJFllYXJbRFNfbGVuZ3RoXSkKIyBJbiBjYXNlIHRoZSBkYXRlcyBhcmUgY29udGFpbmVkIGluIGEgc2luZ2xlIGNvbHVtbiwgKkRhdGUqLCB3ZSB1c2UgdGhlIGZvbGxvd2luZyBzeW50YXguCiMgRmlyc3RfRGF0ZSA8LSBEYXRhX2RmJERhdGVbMV0gCiMgTGFzdF9EYXRlIDwtIERhdGFfZGYkRGF0ZVtEU19sZW5ndGhdCiMgV2Ugc2V0IHRoZSBzY2F0dGVyIHBsb3QgdGl0bGUsIHRoZSBzdWJ0aXRsZSBhbmQgdGhlIGF1dGhvci4KdGl0bGVfY29udGVudCA8LSBicXVvdGUoYXRvcCgiU2NhdHRlciBQbG90IG9mIHRoZSBNb250aGx5IFRveW90YSBDYW1yeSBzYWxlcyBpbiB0aGUgVS5TLiAtIFRyYWluaW5nIGFuZCBUZXN0IFNldHMgLSBmcm9tICIsIC4oRmlyc3RfRGF0ZSksICIgdG8gIiwgLihMYXN0X0RhdGUpKSkKc3VidGl0bGVfY29udGVudCA8LSBicXVvdGUocGFzdGUoIlRyYWluaW5nIHNldCBsZW5ndGggIiwgLihUcm5TX2xlbmd0aCksICIgc2FtcGxlIHBvaW50cy4gVGVzdCBzZXQgbGVuZ3RoICIsIC4oVHN0U19sZW5ndGgpLCAiIHNhbXBsZSBwb2ludHMuIikpCmNhcHRpb25fY29udGVudCA8LSAiQXV0aG9yOiBNYXR0ZW8gQ2hpYWNjaGlhIgojIFdlIHNldCB0aGUgeC1heGlzIG5hbWUuIEhvd2V2ZXIsIGluIHRoaXMgY2FzZSwgc2luY2UgZGF0ZXMgd2lsbCBtYXJrIHRoZSB4LWF4aXMgdGlja3MsIHdlIHRoaW5rIHRoYXQgaXQgaXMgdW5uZWNlc3NhcnkgdG8gZ2l2ZSB0aGUgeC1heGlzIGEgbmFtZS4KeF9uYW1lIDwtIGJxdW90ZSgiIikKIyBUbyBvYnRhaW4gdGhlIHN1Yi1tdWx0aXBsZXMgb2YgdGhlIGxlbmd0aCBvZiB0aGUgZGF0YSBzZXQgYXMgYSBoaW50IG9uIHRoZSBudW1iZXIgb2YgYnJlYWtzIHRvIHVzZSwgd2UgZmFjdG9yaXplIHRoZSBsZW5ndGggb2YgdGhlIHRpbWUgc2VyaWVzLgojIGxpYnJhcnkobnVtYmVycykKIyBwcmltZUZhY3RvcnMoRFNfbGVuZ3RoKQp4X2JyZWFrc19udW0gPC0gMzMKeF9icmVha3NfbWluIDwtIERhdGFfZGYkeFsxXQp4X2JyZWFrc19tYXggPC0gRGF0YV9kZiR4W0RTX2xlbmd0aF0KIyB4X2JpbndpZHRoIDwtIGZsb29yKCh4X2JyZWFrc19tYXgteF9icmVha3NfbWluKS94X2JyZWFrc19udW0pCnhfYmlud2lkdGggPC0gZmxvb3IoRFNfbGVuZ3RoL3hfYnJlYWtzX251bSkKeF9icmVha3MgPC0gc2VxKGZyb209eF9icmVha3NfbWluLCB0bz14X2JyZWFrc19tYXgsIGJ5PXhfYmlud2lkdGgpCmlmKCh4X2JyZWFrc19tYXgtbWF4KHhfYnJlYWtzKSk+eF9iaW53aWR0aC8yKXt4X2JyZWFrcyA8LSBjKHhfYnJlYWtzLHhfYnJlYWtzX21heCl9CnhfbGFicyA8LSBwYXN0ZShEYXRhX2RmJE1vbnRoW3hfYnJlYWtzXSxEYXRhX2RmJFllYXJbeF9icmVha3NdKQpKIDwtIDAuMAp4X2xpbXMgPC0gYyh4X2JyZWFrc19taW4tSip4X2JpbndpZHRoLCB4X2JyZWFrc19tYXgrSip4X2JpbndpZHRoKQp5X25hbWUgPC0gYnF1b3RlKCJNb250aGx5IFNhbGVzIikKeV9icmVha3NfbnVtIDwtIDEwCnlfbWF4IDwtIG1heChuYS5vbWl0KERhdGFfZGYkeSkpCnlfbWluIDwtIG1pbihuYS5vbWl0KERhdGFfZGYkeSkpCnlfYmlud2lkdGggPC0gcm91bmQoKHlfbWF4LXlfbWluKS95X2JyZWFrc19udW0sIGRpZ2l0cz0zKQp5X2JyZWFrc19taW4gPC0geV9taW4teV9iaW53aWR0aAp5X2JyZWFrc19tYXggPC0geV9tYXgreV9iaW53aWR0aAp5X2JyZWFrcyA8LSByb3VuZChzZXEoZnJvbT15X2JyZWFrc19taW4sIHRvPXlfYnJlYWtzX21heCwgYnk9eV9iaW53aWR0aCksMykKIyB5X2JyZWFrc19sb3cgPC0gZmxvb3IoKHlfbWluL3lfYmlud2lkdGgpKSp5X2JpbndpZHRoCiMgeV9icmVha3NfdXAgPC0gY2VpbGluZygoeV9tYXgveV9iaW53aWR0aCkpKnlfYmlud2lkdGgKIyB5X2JyZWFrcyA8LSByb3VuZChzZXEoZnJvbT15X2JyZWFrc19sb3csIHRvPXlfYnJlYWtzX3VwLCBieT15X2JpbndpZHRoKSwzKQp5X2xhYnMgPC0gZm9ybWF0KHlfYnJlYWtzLCBzY2llbnRpZmljPUZBTFNFKQpLIDwtIDAuMAp5X2xpbXMgPC0gYygoeV9icmVha3NfbWluLUsqeV9iaW53aWR0aCksICh5X2JyZWFrc19tYXgrSyp5X2JpbndpZHRoKSkKIyB5X2xpbXMgPC0gYygoeV9icmVha3NfbG93LUsqeV9iaW53aWR0aCksICh5X2JyZWFrc191cCtLKnlfYmlud2lkdGgpKQpjb2xfayA8LSBicXVvdGUoIlRyYWluaW5nIHNldCIpCmNvbF9iIDwtIGJxdW90ZSgiVGVzdCBzZXQiKQpjb2xfZyA8LSBicXVvdGUoIlJlZ3Jlc3Npb24gbGluZSAodHJhaW5pbmcgc2V0KSIpCmNvbF9yIDwtIGJxdW90ZSgiTE9FU1MgY3VydmUgKHRyYWluaW5nIHNldCkiKQpsZWdfbGFicyAgIDwtIGMoY29sX2ssIGNvbF9iLCBjb2xfZywgY29sX3IpCmxlZ19jb2xzICAgPC0gYygiY29sX2siPSJibGFjayIsICJjb2xfYiI9ImJsdWUiLCAiY29sX3IiPSJyZWQiLCAiY29sX2ciPSJncmVlbiIpCmxlZ19icmVha3MgPC0gYygiY29sX2siLCAiY29sX2IiLCAiY29sX2ciLCAiY29sX3IiKQojIGxpYnJhcnkoZ2dwbG90MikKZGZfc3AgPC0gZ2dwbG90KERhdGFfZGYpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9RGF0YV9kZiR4W1RyblNfbGVuZ3RoXSwgbGluZXdpZHRoPTAuMywgY29sb3VyPSJibGFjayIpICsKICBnZW9tX3Ntb290aChkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHggPD0geFtUcm5TX2xlbmd0aF0pLCBhbHBoYT0xLCBsaW5ld2lkdGg9MC43LCBsaW5ldHlwZT0ic29saWQiLCAKICAgICAgICAgICAgICBhZXMoeD14LCB5PXksIGNvbG9yPSJjb2xfZyIpLCBtZXRob2Q9ImxtIiwgZm9ybXVsYT15fngsIHNlPUZBTFNFLCBmdWxscmFuZ2U9RkFMU0UpICsKICBnZW9tX3Ntb290aChkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHggPD0geFtUcm5TX2xlbmd0aF0pLCBhbHBoYT0xLCBsaW5ld2lkdGg9MC43LCBsaW5ldHlwZT0iZGFzaGVkIiwgCiAgICAgICAgICAgICAgYWVzKHg9eCwgeT15LCBjb2xvcj0iY29sX3IiKSwgbWV0aG9kPSJsb2VzcyIsIGZvcm11bGE9eX54LCBzZT1GQUxTRSkgKwogIGdlb21fcG9pbnQoZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR4IDw9IHhbVHJuU19sZW5ndGhdKSwgYWxwaGE9MSwgc2l6ZT0wLjUsIHNoYXBlPTE5LCAKICAgICAgICAgICAgIGFlcyh4PXgsIHk9eSwgY29sb3I9ImNvbF9rIikpICsKICBnZW9tX3BvaW50KGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkeCA+IHhbVHJuU19sZW5ndGhdKSwgYWxwaGE9MSwgc2l6ZT0wLjUsIHNoYXBlPTE5LCAKICAgICAgICAgICAgIGFlcyh4PXgsIHk9eSwgY29sb3I9ImNvbF9iIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZT14X25hbWUsIGJyZWFrcz14X2JyZWFrcywgbGFiZWw9eF9sYWJzLCBsaW1pdHM9eF9saW1zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWU9eV9uYW1lLCBicmVha3M9eV9icmVha3MsIGxhYmVscz1OVUxMLCBsaW1pdHM9eV9saW1zLAogICAgICAgICAgICAgICAgICAgICBzZWMuYXhpcz1zZWNfYXhpcyh+LiwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9eV9sYWJzKSkgKwogIGdndGl0bGUodGl0bGVfY29udGVudCkgKwogIGxhYnMoc3VidGl0bGU9c3VidGl0bGVfY29udGVudCwgY2FwdGlvbj1jYXB0aW9uX2NvbnRlbnQpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWU9IkxlZ2VuZCIsIGxhYmVscz1sZWdfbGFicywgdmFsdWVzPWxlZ19jb2xzLCBicmVha3M9bGVnX2JyZWFrcywKICAgICAgICAgICAgICAgICAgICAgIGd1aWRlPWd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXM9bGlzdChzaGFwZT1jKDE5LDE5LE5BLE5BKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGU9YygiYmxhbmsiLCAiYmxhbmsiLCAic29saWQiLCAiZGFzaGVkIikpKSkgKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSksCiAgICAgICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9IDAuNSksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPS00NSwgdmp1c3Q9MSwgaGp1c3Q9LTAuMyksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aD11bml0KDAuOCwiY20iKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpwbG90KGRmX3NwKQpgYGAKZSB0cmFtaXRlIGlsICoqTGluZSBQbG90KioKYGBge3J9CnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKGF0b3AoIkxpbmUgUGxvdCBvZiB0aGUgTW9udGhseSBUb3lvdGEgQ2Ftcnkgc2FsZXMgaW4gdGhlIFUuUy4gLSBUcmFpbmluZyBhbmQgVGVzdCBTZXRzIC0gZnJvbSAiLCAuKEZpcnN0X0RhdGUpLCAiIHRvICIsIC4oTGFzdF9EYXRlKSkpCiMgV2UgZHJhdyB0aGUgbGluZSBwbG90LgpkZl9scCA8LSBnZ3Bsb3QoRGF0YV9kZikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD1EYXRhX2RmJHhbVHJuU19sZW5ndGhdLCBsaW5ld2lkdGg9MC4zLCBjb2xvdXI9ImJsYWNrIikgKwogIGdlb21fc21vb3RoKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkeCA8PSB4W1RyblNfbGVuZ3RoXSksIGFscGhhPTEsIGxpbmV3aWR0aD0wLjcsIGxpbmV0eXBlPSJzb2xpZCIsIAogICAgICAgICAgICAgIGFlcyh4PXgsIHk9eSwgY29sb3I9ImNvbF9nIiksIG1ldGhvZD0ibG0iLCBmb3JtdWxhPXl+eCwgc2U9RkFMU0UsIGZ1bGxyYW5nZT1GQUxTRSkgKwogIGdlb21fc21vb3RoKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkeCA8PSB4W1RyblNfbGVuZ3RoXSksIGFscGhhPTEsIGxpbmV3aWR0aD0wLjcsIGxpbmV0eXBlPSJkYXNoZWQiLCAKICAgICAgICAgICAgICBhZXMoeD14LCB5PXksIGNvbG9yPSJjb2xfciIpLCBtZXRob2Q9ImxvZXNzIiwgZm9ybXVsYT15fngsIHNlPUZBTFNFKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkeCA8PSB4W1RyblNfbGVuZ3RoXSksIGFscGhhPTEsIGxpbmV3aWR0aD0wLjUsIGxpbmV0eXBlPSJzb2xpZCIsIAogICAgICAgICAgICBhZXMoeD14LCB5PXksIGNvbG9yPSJjb2xfayIsIGdyb3VwPTEpKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkeCA+PSB4W1RyblNfbGVuZ3RoXSksIGFscGhhPTEsIGxpbmV3aWR0aD0wLjUsIGxpbmV0eXBlPSJzb2xpZCIsIAogICAgICAgICAgICBhZXMoeD14LCB5PXksIGNvbG9yPSJjb2xfYiIsIGdyb3VwPTEpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9eF9uYW1lLCBicmVha3M9eF9icmVha3MsIGxhYmVsPXhfbGFicywgbGltaXRzPXhfbGltcykgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lPXlfbmFtZSwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXlfbGltcywKICAgICAgICAgICAgICAgICAgICAgc2VjLmF4aXM9c2VjX2F4aXMofi4sIGJyZWFrcz15X2JyZWFrcywgbGFiZWxzPXlfbGFicykpICsKICBnZ3RpdGxlKHRpdGxlX2NvbnRlbnQpICsKICBsYWJzKHN1YnRpdGxlPXN1YnRpdGxlX2NvbnRlbnQsIGNhcHRpb249Y2FwdGlvbl9jb250ZW50KSArCiAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lPSJMZWdlbmQiLCBsYWJlbHM9bGVnX2xhYnMsIHZhbHVlcz1sZWdfY29scywgYnJlYWtzPWxlZ19icmVha3MsCiAgICAgICAgICAgICAgICAgICAgICBndWlkZT1ndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzPWxpc3QobGluZXR5cGU9Yygic29saWQiLCAic29saWQiLCAic29saWQiLCAiZGFzaGVkIikpKSkgKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSksCiAgICAgICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MC41KSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9LTQ1LCB2anVzdD0xLCBoanVzdD0tMC4zKSwKICAgICAgICBsZWdlbmQua2V5LndpZHRoPXVuaXQoMC44LCJjbSIpLCBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpCnBsb3QoZGZfbHApCmBgYApEYSB1bidpbml6aWFsZSBpc3BlemlvbmUgdmlzaXZhIGRlaSBncmFmaWNpIHNpIG5vdGEgY29tZSBsYSBzZXJpZSBzdG9yaWNhIG5vbiBhYmJpYSB1biBhbmRhbWVudG8gbGluZWFyZSBuZWwgdGVtcG8uIFNpIHB1w7Igc3VwcG9ycmUgdW5hIHByZXNlbnphIGRpIHVuYSBjb21wb25lbnRlICpzdGFnaW9uYWxlKiBlIGRpIHVuICp0cmVuZCogY2hlIGluaXppYWxtZW50ZSDDqCBjcmVzY2VudGUgcGVyIGRpdmVudGFyZSBpbiBzZWd1aXRvIGRlY3Jlc2NlbnRlLgpOb24gc2VtYnJhIGVzc2VyZSBwcmVzZW50ZSB1bidlbGV2YXRhIGV0ZXJvc2NoZWRhY2l0w6AsIGVzc2VuZG8gbG8gKnNwcmVhZCogZGVsICpwYXRoKiBkZWxsYSBzZXJpZSBzdG9yaWNhIGFsbCdpbmNpcmNhIHNlbXByZSBzaW1pbGUuIFF1ZXN0aSBzdHVkaSwgY29tdW5xdWUsIHZlcnJhbm5vIGVmZmV0dHVhdGkgaW4gc2VndWl0by4KCiMjIyMgTGluZWFyIE1vZGVsCgpMJ2lkZWEgaW5pemlhbGUgw6ggcXVlbGxhIGRpIGNvbnNpZGVyYXJlIHVuIHNlbXBsaWNlICoqcHJlZGl0dG9yZSBsaW5lYXJlKiouCmBgYHtyfQpDYW1yeV9sbSA8LSBsbShkZiRzYWxlc1sxOlRyblNfbGVuZ3RoXX50WzE6VHJuU19sZW5ndGhdLCBkYXRhPWRmKQpDYW1yeV9sbV9zdW1tIDwtIHN1bW1hcnkoQ2FtcnlfbG0pCnNob3coQ2FtcnlfbG1fc3VtbSkKYGBgClBlciB2ZXJpZmljYXJlIGxhIGNvcnJldHRlenphIGRlbCBtb2RlbGxvIGxpbmVhcmUgc2kgYW5hbGl6emFubyBpIHN1b2kgcmVzaWR1aSBlIHNpIHN0dWRpYW5vOgoKLSAqKk9tb3NjaGVkYXN0aWNpdMOgKio6ICBzaWduaWZpY2EgY2hlIGkgcmVzaWR1aSBzb25vIGVxdWFtZW50ZSBkaXN0cmlidWl0aSBsdW5nbyBsYSBsaW5lYSBkaSByZWdyZXNzaW9uZSwgb3Z2ZXJvIHNvcHJhIGUgc290dG8gbGEgbGluZWEgZGkgcmVncmVzc2lvbmUgZSBsYSB2YXJpYW56YSBkZWkgcmVzaWR1aSBkb3ZyZWJiZSBlc3NlcmUgbGEgc3Rlc3NhIHBlciB0dXR0aSBpIHB1bnRlZ2dpIHByZXZpc3RpIGx1bmdvIGxhIGxpbmVhIGRpIHJlZ3Jlc3Npb25lLgotICoqQXNzZW56YSBkaSBhdXRvY29ycmVsYXppb25lKio6IEwnYXV0b2NvcnJlbGF6aW9uZSBzaSB2ZXJpZmljYSBxdWFuZG8gaSByZXNpZHVpIG5vbiBzb25vIGluZGlwZW5kZW50aSBsJ3VubyBkYWxsJ2FsdHJvLgotICoqU3RhemlvbmFyaWV0w6AqKjogdW4gbW9kZWxsbyBkaSBwcmVkaXppb25lIGNvbiByZXNpZHVpIHN0YXppb25hcmkgZ2FyYW50aXNjZSBjaGUgbGUgcHJldmlzaW9uaSBmdXR1cmUgc2lhbm8gYWZmaWRhYmlsaSBlIG5vbiBpbmZsdWVuemF0ZSBkYSBmbHV0dHVhemlvbmkgY2FzdWFsaSBvIHRlbmRlbnplIHRlbXBvcmFsaS4KLSAqKkdhdXNzaWFuaXTDoCoqOiBzZSBpIHJlc2lkdWkgc2VndW9ubyB1bmEgZGlzdHJpYnV6aW9uZSBub3JtYWxlLgpgYGB7cn0KQ2FtcnlfbG1fZml0IDwtIENhbXJ5X2xtW1siZml0dGVkLnZhbHVlcyJdXSAgIApDYW1yeV9sbV9yZXMgPC0gQ2FtcnlfbG1bWyJyZXNpZHVhbHMiXV0gICAgICAgCkNhbXJ5X2RlZ2ZyIDwtIENhbXJ5X2xtW1siZGYucmVzaWR1YWwiXV0gICAgICAKeV9yZXMgPC0gYXMudmVjdG9yKENhbXJ5X2xtX3JlcykKcGxvdChDYW1yeV9sbSwxKQpgYGAKSW4gcXVlc3RvIGNhc28sIGRhbCBncmFmaWNvICIqUmVzaWR1YWxzIHZzIEZpdHRlZCoiLCBhYmJpYW1vIHVuYSBwcm92YSB2aXNpdmEgZGVsbGEgbm9uIHN0YXppb25hcmlldMOgIG1lZGlhIGRlaSByZXNpZHVpLCBpbCBjaGUgc2lnbmlmaWNhIGNoZSBpbCBtb2RlbGxvIGxpbmVhcmUgbm9uIHNlbWJyYSBpbiBncmFkbyBkaSBzcGllZ2FyZSBpbCAqdHJlbmQqLiBEJ2FsdHJvIGNhbnRvLCBsYSBwcm92YSB2aXNpdmEgZGVsbCdvbW9zY2hlZGFzdGljaXTDoCDDqCBjb25mZXJtYXRhOiBsYSBkaWZmdXNpb25lIGRlaSByZXNpZHVpIGludG9ybm8gYWxsYSBsaW5lYSBMT0VTUyBub24gc2VtYnJhIGF1bWVudGFyZS4KYGBge3J9CnBsb3QoQ2FtcnlfbG0sMikKYGBgCkRhbCAqUS1RIHBsb3QqIHNpIGhhIHVuYSBmb3J0ZSBwcm92YSB2aXNpdmEgZGVsbGEgbm9uICpnYXVzc2lhbml0w6AqIGRlaSByZXNpZHVpIGRlbCBtb2RlbGxvIGxpbmVhcmUuIAoKUGVyIHRlc3RhcmUgY29tcHV0YXppb25hbG1lbnRlIGxhIHN0YXppb25hcmlldMOgIGRlaSByZXNpZHVpLCBzaSB1dGlsaXp6YW5vIHNvbGl0YW1lbnRlIGR1ZSB0ZXN0OiBpbCB0ZXN0IGRpIEF1Z21lbnRlZCBEaWNrZXktRnVsbGVyICgqQURGKikgZSBpbCB0ZXN0IGRpIEt3aWF0b3dza2ktUGhpbGxpcHMtU2NobWlkdC1TaGluICgqS1BTUyopLiBJbCB0ZXN0ICoqQURGIGFzc3VtZSBsJ2lwb3Rlc2kgbnVsbGEgZGkgbm9uIHN0YXppb25hcmlldMOgKiouIEluIG1vZG8gcGnDuSBzcGVjaWZpY28sIGlsIHRlc3QgQURGIGFzc3VtZSBjaGUgbGEgc2VyaWUgdGVtcG9yYWxlIHNpYSBnZW5lcmF0YSBkYSB1biBwcm9jZXNzbyBzdG9jYXN0aWNvIGNvbiB1biBjb21wb25lbnRlIGRpIHJhbmRvbSB3YWxrLkFsIGNvbnRyYXJpbywgaWwgdGVzdCAqKktQU1MgYXNzdW1lIGwnaXBvdGVzaSBudWxsYSBkaSBzdGF6aW9uYXJpZXTDoCoqLiBJbmZhdHRpLCBpbCB0ZXN0IEtQU1MgYXNzdW1lIGNoZSBsYSBzZXJpZSB0ZW1wb3JhbGUgc2lhIGdlbmVyYXRhIGRhIHVuIHByb2Nlc3NvIGF1dG9yZWdyZXNzaXZvLiBRdWFuZG8gaWwgdGVzdCAqQURGKiByZXNwaW5nZSBsJ2lwb3Rlc2kgbnVsbGEgZSAqS1BTUyogbm8sIGFiYmlhbW8gcHJvdmUgZGkgc3RhemlvbmFyaWV0w6AgbmVsbGEgc2VyaWUgdGVtcG9yYWxlLiBRdWFuZG8gaWwgdGVzdCAqQURGKiBub24gcmVzcGluZ2UgbCdpcG90ZXNpIG51bGxhIGUgKktQU1MqIHPDrCwgYWJiaWFtbyBwcm92ZSBkaSBub24gc3RhemlvbmFyaWV0w6AuIEFsdHJpIGNhc2kgc29ubyBjb25zaWRlcmF0aSBkdWJiaS4KCipERiB0ZXN0KjoKYGBge3J9CiB5IDwtIENhbXJ5X2xtW1sicmVzaWR1YWxzIl1dICAgIyBUaGUgZGF0YSBzZXQgdG8gYmUgdGVzdGVkLgpudW1fbGFncyA8LSAwICAgICAgICAgICAgICAgICAgICMgU2V0dGluZyB0aGUgbGFnIHBhcmFtZXRlciBmb3IgdGhlIHRlc3QuCkNhbXJ5X2xtX3Jlc19ERl9ub25lIDwtIHVyLmRmKHksIHR5cGU9Im5vbmUiLCBsYWdzPW51bV9sYWdzLCBzZWxlY3RsYWdzPSJGaXhlZCIpICAgIApzdW1tYXJ5KENhbXJ5X2xtX3Jlc19ERl9ub25lKSAgIApgYGAKCkxhIHN0YXRpc3RpY2EgZGVsIHRlc3QgREYgYXNzdW1lIHVuIHZhbG9yZSBhbGwnaW50ZXJubyBkZWxsYSByZWdpb25lIGRpIHJpZ2V0dG8gYWwgbGl2ZWxsbyBkaSBzaWduaWZpY2F0aXZpdMOgIM6xPTAsMDEgbyDOsT0xJS4gUGVydGFudG8sIHBvc3NpYW1vIHJpZ2V0dGFyZSBsJ2lwb3Rlc2kgbnVsbGEgaW4gZmF2b3JlIGRlbGwnYWx0ZXJuYXRpdmEgZGkgc3RhemlvbmFyaWV0w6AgbWVkaWEuCgoqS1BTUyB0ZXN0KjoKYGBge3J9CnkgPC0gQ2FtcnlfbG1bWyJyZXNpZHVhbHMiXV0gICAgIyBUaGUgZGF0YSBzZXQgdG8gYmUgdGVzdGVkCkNhbXJ5X2xtX3Jlc19LUFNTX211IDwtIHVyLmtwc3MoeSwgdHlwZT0ibXUiLCBsYWdzPSJuaWwiLCB1c2UubGFnPU5VTEwpICAgIApzdW1tYXJ5KENhbXJ5X2xtX3Jlc19LUFNTX211KSAgICAjIFNob3dpbmcgdGhlIHJlc3VsdCBvZiB0aGUgdGVzdApgYGAKCklsIHZhbG9yZSBkZWxsYSBzdGF0aXN0aWNhIGRlbCB0ZXN0IEtQU1Mgw6ggbWFnZ2lvcmUgZGVsIHZhbG9yZSBjcml0aWNvIHBlciBpbCBsaXZlbGxvIGRpIHNpZ25pZmljYXRpdml0w6AgZGVsbCcxJSwgaWwgY2hlIHNpZ25pZmljYSBjaGUgZG9iYmlhbW8gcmlnZXR0YXJlIGwnaXBvdGVzaSBudWxsYSBkaSBzdGF6aW9uYXJpZXTDoCBtZWRpYS4gUXVlc3RvIHN1Z2dlcmlzY2UgY2hlIGxhIHNlcmllIHRlbXBvcmFsZSDDqCBwcm9iYWJpbG1lbnRlIG5vbiBzdGF6aW9uYXJpYS4KSSB0ZXN0IGVmZmV0dHVhdGkgbm9uIGNpIGRhbm5vIHVuYSBjZXJ0ZXp6YSBldmlkZW50ZSBkZWxsYSBzdGF6aW9uYXJpZXTDoCBtZWRpYSBkZWkgcmVzaWR1aSwgZGkgY29uc2VndWVuemEgaWwgbW9kZWxsbyBsaW5lYXJlIG5vbiBwdcOyIGVzc2VyZSBjb25zaWRlcmF0byBhdHRlbmRpYmlsZS4KCkkgdGVzdCBjb21wdXRhemlvbmFsaSBjaGUgdmVuZ29ubyBzb2xpdGFtZW50ZSBhcHBsaWNhdGkgcGVyIHJpbGV2YXJlIGwnZXRlcm9zY2hlZGFzdGljaXTDoCBuZWxsZSBzZXJpZSB0ZW1wb3JhbGkgc29ubyBpIHRlc3QgZGkgKipCcmV1c2NoLVBhZ2FuIChCUCkqKiBlICoqV2hpdGUgKFcpKiouIAoKQWJiaWFtbzogCi0gaXBvdGVzaSBudWxsYSAtIHZhcmlhbnplIHVndWFsaS9jb3N0YW50aSBuZWkgdGVybWluaSBkaSBlcnJvcmU7Ci0gaXBvdGVzaSBhbHRlcm5hdGl2YSAtIHZhcmlhbnplIG5vbiB1Z3VhbGkvbm9uIGNvc3RhbnRpIG5laSB0ZXJtaW5pIGRpIGVycm9yZS4KCkJQIGUgVyBzb25vIHRlc3QgJM+HXjIkLiBQacO5IGFsdG8gw6ggaWwgdmFsb3JlIGRpICTPh14yJCwgZXF1aXZhbGVudGVtZW50ZSBwacO5IGJhc3NvIMOoIGlsICpwLXZhbHVlIChQcm8gYj4gQ2hpMikqLCBtZW5vIHByb2JhYmlsZSBjaGUgaSB0ZXJtaW5pIGRpIGVycm9yZSBzaWFubyBvbW9nZW5laS4KCkwnb3B6aW9uZSAqc3R1ZGVudGl6ZSogw6ggaW1wb3J0YW50ZSBxdWFuZG8gc2kgdHJhdHRhIGRpIHJlc2lkdWkgY29uIGRpc3RyaWJ1emlvbmUgaGVhdnkgdGFpbGVkIChnbGkgZXZlbnRpIHJhcmkgaGFubm8gdW5hIHByb2JhYmlsaXTDoCByZWxhdGl2YW1lbnRlIGFsdGEgZGkgdmVyaWZpY2Fyc2kpLiBMJ29wemlvbmUgKnZhcmZvcm11bGEqIGNvbnNlbnRlIGwnaW50cm9kdXppb25lIGRlbCB0ZXN0IFdoaXRlLgoKKkJSRVVTQ0gtUEFHQU4gdGVzdCo6CmBgYHtyfQojIFRoZSBzdHVkZW50aXplZCBCcmV1c2NoLVBhZ2FuIHRlc3QKdCA8LSAxOmxlbmd0aCh5X3JlcykKeV9yZXNfc3R1ZF9CUCA8LSBsbXRlc3Q6OmJwdGVzdChmb3JtdWxhPXlfcmVzfnQsIHZhcmZvcm11bGE9TlVMTCwgc3R1ZGVudGl6ZT1UUlVFLCBkYXRhPU5VTEwpCnNob3coeV9yZXNfc3R1ZF9CUCkKYGBgCgpVbiBwLXZhbHVlIGVsZXZhdG8sIGNvbWUgcXVlc3RvIChzdXBlcmlvcmUgYSAwLjA1KSwgaW5kaWNhIGNoZSBub24gY2kgc29ubyBwcm92ZSBzdWZmaWNpZW50aSBwZXIgcmVzcGluZ2VyZSBsJ2lwb3Rlc2kgbnVsbGEgZGkgb21vc2NoZWRhc3RpY2l0w6AuIFF1ZXN0byBzaWduaWZpY2EgY2hlIG5vbiDDqCBwb3NzaWJpbGUgYWZmZXJtYXJlIGNvbiBjZXJ0ZXp6YSBjaGUgbGEgdmFyaWFuemEgZGVpIHRlcm1pbmkgZGkgZXJyb3JlIG5vbiBzaWEgY29zdGFudGUgZSBjaGUsIHF1aW5kaSwgc2lhIHByZXNlbnRlIGV0ZXJvc2NoZWRhc3RpY2l0w6AuCgoqV0hJVEUgdGVzdCo6CmBgYHtyfQojIFRoZSBzdHVkZW50aXplZCBXaGl0ZSB0ZXN0CnQgPC0gMTpsZW5ndGgoeV9yZXMpCnlfcmVzX3N0dWRfVyA8LSBsbXRlc3Q6OmJwdGVzdChmb3JtdWxhPXlfcmVzfnQsIHZhcmZvcm11bGE9eV9yZXN+dCtJKHReMiksIHN0dWRlbnRpemU9VFJVRSwgZGF0YT1OVUxMKQpzaG93KHlfcmVzX3N0dWRfVykKYGBgClVucC12YWx1ZSBlbGV2YXRvIGNvbWUgcXVlc3RvIChzdXBlcmlvcmUgYSAwLjA1KSwgaW5kaWNhIGNoZSBub24gY2kgc29ubyBwcm92ZSBzdWZmaWNpZW50aSBwZXIgcmVzcGluZ2VyZSBsJ2lwb3Rlc2kgbnVsbGEgZGkgb21vc2NoZWRhc3RpY2l0w6AuIFF1ZXN0byBzaWduaWZpY2EgY2hlIG5vbiDDqCBwb3NzaWJpbGUgYWZmZXJtYXJlIGNvbiBjZXJ0ZXp6YSBjaGUgbGEgdmFyaWFuemEgZGVpIHRlcm1pbmkgZGkgZXJyb3JlIG5vbiBzaWEgY29zdGFudGUgZSBjaGUsIHF1aW5kaSwgc2lhIHByZXNlbnRlIGV0ZXJvc2NoZWRhc3RpY2l0w6AuCgpEYWkgdGVzdCBhcHBlbmEgc3ZvbHRpIHNpIHB1w7IgY29uZmVybWFyZSBjaGUgaSByZXNpZHVpIHNvbm8gZ2VuZXJhdGkgZGEgdW4gcnVtb3JlIG9tb3NjaGVkYXN0aWNvLCBjb21lIGNpIHNpIGVyYSBnacOgIGFjY29ydGkgaW4gcHJlY2VkZW56YSBhbmFsaXp6YW5kbyB2aXNpdmFtZW50ZSBpbCBncmFmaWNvICJSZXNpZHVhbHMgdnMgRml0dGVkIi4KClBsb3QgZGVsbCdhdXRvY29ycmVsb2dyYW1tYQpgYGB7cn0KIyBQbG90IG9mIHRoZSBhdXRvY29ycmVsb2dyYW0uCnkgPC0gQ2FtcnlfbG0kcmVzaWR1YWxzClQgPC0gbGVuZ3RoKHkpCiMgbWF4bGFnIDwtIGNlaWxpbmcoMTAqbG9nMTAoVCkpICAgICMgRGVmYXVsdAojIG1heGxhZyA8LSBjZWlsaW5nKHNxcnQoVCkrNDUpICAgICAjIEJveC1KZW5raW5zCiMgbWF4bGFnIDwtIGNlaWxpbmcobWluKDEwLCBULzQpKSAgICMgSHluZG1hbiAoZm9yIGRhdGEgd2l0aG91dCBzZWFzb25hbGl0eSkKbWF4bGFnIDwtIGNlaWxpbmcobWluKDIqMTIsIFQvNSkpICAgIyBIeW5kbWFuIChmb3IgZGF0YSB3aXRoIHNlYXNvbmFsaXR5KQojIGh0dHBzOi8vcm9iamh5bmRtYW4uY29tL2h5bmRzaWdodC9sanVuZy1ib3gtdGVzdC8KQXV0X0Z1bl95IDwtIGFjZih5LCBsYWcubWF4PW1heGxhZywgdHlwZT0iY29ycmVsYXRpb24iLCBwbG90PUZBTFNFKQpjaV85MCA8LSBxbm9ybSgoMSswLjkwKS8yKS9zcXJ0KFQpCmNpXzk1IDwtIHFub3JtKCgxKzAuOTUpLzIpL3NxcnQoVCkKY2lfOTkgPC0gcW5vcm0oKDErMC45OSkvMikvc3FydChUKQpQbG90X0F1dF9GdW5feSA8LSBkYXRhLmZyYW1lKGxhZz1BdXRfRnVuX3kkbGFnLCBhY2Y9QXV0X0Z1bl95JGFjZikKRmlyc3RfRGF0ZSA8LSBwYXN0ZShEYXRhX2RmJE1vbnRoWzFdLERhdGFfZGYkWWVhclsxXSkKTGFzdF9EYXRlIDwtIHBhc3RlKERhdGFfZGYkTW9udGhbVF0sRGF0YV9kZiRZZWFyW1RdKQp0aXRsZV9jb250ZW50IDwtIGJxdW90ZShhdG9wKCJBdXRvY29ycmVsb2dyYW0gb2YgdGhlIFJlc2lkdWFscyBvZiB0aGUgTGluZWFyIE1vZGVsIGZvciBNb250aGx5IFRveW90YSBDYW1yeSBzYWxlcyBpbiB0aGUgVS5TLiBmcm9tICIsIC4oRmlyc3RfRGF0ZSksICIgdG8gIiwgLihMYXN0X0RhdGUpKSkKc3VidGl0bGVfY29udGVudCA8LSBicXVvdGUocGFzdGUoIlBhdGggbGVuZ3RoICIsIC4oVCksICIgc2FtcGxlIHBvaW50cy4gTGFncyAiLCAuKG1heGxhZykpKQpjYXB0aW9uX2NvbnRlbnQgPC0gIkF1dGhvcjogTWF0dGVvIENoaWFjY2hpYSIKeF9uYW1lIDwtIGJxdW90ZSgibGFncyIpCnhfYnJlYWtzX251bSA8LSBtYXhsYWcKeF9iaW53aWR0aCA8LSAxCnhfYnJlYWtzIDwtIEF1dF9GdW5feSRsYWcKeF9sYWJzIDwtIGZvcm1hdCh4X2JyZWFrcywgc2NpZW50aWZpYz1GQUxTRSkKZ2dwbG90KFBsb3RfQXV0X0Z1bl95LCBhZXMoeD1sYWcsIHk9YWNmKSkrCiAgZ2VvbV9zZWdtZW50KGFlcyh4PWxhZywgeT1yZXAoMCxsZW5ndGgobGFnKSksIHhlbmQ9bGFnLCB5ZW5kPWFjZiksIGxpbmV3aWR0aD0xLCBjb2w9ImJsYWNrIikgKwogICMgZ2VvbV9jb2wobWFwcGluZz1OVUxMLCBkYXRhPU5VTEwsIHBvc2l0aW9uPSJkb2RnZSIsIHdpZHRoPTAuMSwgY29sPSJibGFjayIsIGluaGVyaXQuYWVzPVRSVUUpKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9LWNpXzkwLCBjb2xvcj0iQ0lfOTAiKSwgc2hvdy5sZWdlbmQ9VFJVRSwgbHR5PTMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PWNpXzkwLCBjb2xvcj0iQ0lfOTAiKSwgbHR5PTMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PWNpXzk1LCBjb2xvcj0iQ0lfOTUiKSwgc2hvdy5sZWdlbmQ9VFJVRSwgbHR5PTQpKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9LWNpXzk1LCBjb2xvcj0iQ0lfOTUiKSwgbHR5PTQpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PS1jaV85OSwgY29sb3I9IkNJXzk5IiksIHNob3cubGVnZW5kPVRSVUUsIGx0eT00KSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD1jaV85OSwgY29sb3I9IkNJXzk5IiksIGx0eT00KSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9ImxhZyIsIGJyZWFrcz14X2JyZWFrcywgbGFiZWw9eF9sYWJzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWU9ImFjZiB2YWx1ZSIsIGJyZWFrcz13YWl2ZXIoKSwgbGFiZWxzPU5VTEwsCiAgICAgICAgICAgICAgICAgICAgIHNlYy5heGlzPXNlY19heGlzKH4uLCBicmVha3M9d2FpdmVyKCksIGxhYmVscz13YWl2ZXIoKSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iQ29uZi4gSW50ZXIuIiwgbGFiZWxzPWMoIjkwJSIsIjk1JSIsIjk5JSIpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXM9YyhDSV85MD0iZ3JlZW4iLCBDSV85NT0iYmx1ZSIsIENJXzk5PSJyZWQiKSkgKwogIGdndGl0bGUodGl0bGVfY29udGVudCkgKwogIGxhYnMoc3VidGl0bGU9c3VidGl0bGVfY29udGVudCwgY2FwdGlvbj1jYXB0aW9uX2NvbnRlbnQpICsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUpLCAKICAgICAgICBwbG90LnN1YnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0gMC41KSwKICAgICAgICBwbG90LmNhcHRpb249ZWxlbWVudF90ZXh0KGhqdXN0PTEuMCksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aD11bml0KDAuOCwiY20iKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpgYGAKU2kgbm90YSBkYWwgZ3JhZmljbyB1bmEgZm9ydGUgYXV0b2NvcnJlbGF6aW9uZS4KUGVyIGNvbXBsZXRlenphIHNpIGVzZWd1ZSBpbCAqTGppdW5nLWJveCB0ZXN0Ki4KYGBge3J9CnkgPC0geV9yZXMKbWF4bGFnIDwtIGNlaWxpbmcobWluKDIqMTIsIFQvNSkpICAgICAjIEh5bmRtYW4gaHR0cHM6Ly9yb2JqaHluZG1hbi5jb20vaHluZHNpZ2h0L2xqdW5nLWJveC10ZXN0LwpCb3gudGVzdCh5LCBsYWcgPSBtYXhsYWcsIHR5cGUgPSAiTGp1bmctQm94IiwgZml0ZGYgPSAwKQpgYGAKSWwgcC12YWx1ZSBpbmZlcmlvcmUgYSAkMi4yICogMTBeey0xNn0kIGluZGljYSBjaGUgYyfDqCB1bmEgZm9ydGUgZXZpZGVuemEgY2hlIGkgcmVzaWR1aSBub24gc29ubyBpbmRpcGVuZGVudGkgZSBjaGUgZXNpc3RlIHVuYSBmb3JtYSBkaSBhdXRvY29ycmVsYXppb25lIHNlcmlhbGUgbmVpIGRhdGkuIFBvc3NpYW1vIHF1aW5kaSBhZmZlcm1hcmUsIGluIGxpbmVhIGNvbiBsJ2V2aWRlbnphIHZpc2l2YSBkZWxsJ2F1dG9jb3JyZWxvZ3JhbW1hIGUgY29uIGlsIHJpc3VsdGF0byBkZWwgKkxqdW5nLUJveCB0ZXN0KiwgY2hlIGkgcmVzaWR1aSBkZWwgbW9kZWxsbyBsaW5lYXJlICoqbm9uKiogc29ubyBnZW5lcmF0aSBkYSB1biBydW1vcmUgY29uIGRpc3RyaWJ1emlvbmUgaW5kaXBlbmRlbnRlIGUgaWRlbnRpY2FtZW50ZSBkaXN0cmlidWl0YS4KCkdyYXppZSBhaSByaXN1bHRhdGkgYXBwZW5hIG90dGVudXRpLCBwb3NzaWFtbyByaWdldHRhcmUgaWwgbW9kZWxsbyBsaW5lYXJlIGNvbWUgbW9kZWxsbyBkaSBwcmVkaXppb25lIGRhIHV0aWxpenphcmUuCgojIyMjIFNUTCBNb2RlbAoKSWwgcHJpbW8gcGFzc28gw6ggcXVlbGxvIGRpIGlzb2xhcmUgaWwgKlRyYWluIFNldCogcGVyIHBvdGVybG8gdXRpbGl6emFyZSBuZWxsbCdhbmFsaXNpLiAKSWwgbW9kZWxsbyBvdHRlbnV0byB2ZXJyw6AgcG9pIHV0aWxpenphdG8gbmVsbGEgcHJlZGl6aW9uZSBkZWkgdmFsb3JpIHJlbGF0aXZpIGFsICpUZXN0IFNldCogcGVyIHBvaSBwcm9jZWRlcmUgY29uIGwndXRpbGl6em8gZGkgdmFyaWUgc3RhdGlzdGljaGUgZGkgYWNjdXJhdGV6emEgcGVyIHZhbHV0YXJlIGxhIHByZWRpemlvbmUuIApgYGB7cn0KI3RyYWluc2V0IGNyZWF0aW9uCkNhbXJ5X1RyblNfZGYgPC0gZGZbMTpUcm5TX2xlbmd0aCxdCkNhbXJ5X1RzdFNfZGYgPC0gZGZbVHJuU19sZW5ndGgrMTogVHJuU19sZW5ndGgrVHN0U19sZW5ndGgsIF0KaGVhZChDYW1yeV9Ucm5TX2RmKQp0YWlsKENhbXJ5X1RyblNfZGYpCmBgYAoKIyMjIyMgQm94LUNveCB0cmFzZm9ybWF0aW9ucwoKTGUgdHJhc2Zvcm1hemlvbmkgZGkgKkJveC1Db3gqIGEgY3VpIHZpZW5lIHNvdHRvcG9zdG8gaWwgKlRyYWluIFNldCogc29ubzoKCi0gVHJhc2Zvcm1hemlvbmUgZGkgQm94LUNveCBjb2wgbWV0b2RvIEd1ZXJyZXJvCi0gVHJhc2Zvcm1hemlvbmUgZGkgQm94LUNveCBjb2wgbWV0b2RvIGRlbGxhIG1hc3NpbWEgdmVyb3NpbWlnbGlhbnphCi0gVHJhZm9ybWF6aW9uZSBsb2dhcml0bWljYQotIFRyYXNmb3JtYXppb25lIHF1YWRyYXRpY2EgaW52ZXJzYQpgYGB7cn0KeSA8LSBDYW1yeV9Ucm5TX2RmJHNhbGVzCnlfQkNUX0d1ZXJyX2xhbWJkYSA8LSBmb3JlY2FzdDo6Qm94Q294LmxhbWJkYSh5LCBtZXRob2Q9Imd1ZXJyZXJvIikKdGlsZGVfeV9CQ1RfR3VlcnJfbGFtYmRhIDwtIGZvcmVjYXN0OjpCb3hDb3goeSwgbGFtYmRhPXlfQkNUX0d1ZXJyX2xhbWJkYSkKeV9CQ1RfbG9nbGlrX2xhbWJkYSA8LSBmb3JlY2FzdDo6Qm94Q294LmxhbWJkYSh5LCBtZXRob2Q9ImxvZ2xpayIpCnRpbGRlX3lfQkNUX2xvZ2xpa19sYW1iZGEgPC0gZm9yZWNhc3Q6OkJveENveCh5LCBsYW1iZGE9eV9CQ1RfbG9nbGlrX2xhbWJkYSkKYGBgClVuYSB2b2x0YSBjYWxjb2xhdGUgbGUgdHJhc2Zvcm1hdGUsIHZlbmdvbm8gYWdnaXVudGkgaSB2YWxvcmkgYWwgZGF0YWZyYW1lLgpgYGB7cn0KQ2FtcnlfVHJuU19kZiA8LSBhZGRfY29sdW1uKENhbXJ5X1RyblNfZGYsIHlfQkNUX0d1ZXJyPXRpbGRlX3lfQkNUX0d1ZXJyX2xhbWJkYSwgeV9CQ1RfbG9nbGlrPXRpbGRlX3lfQkNUX2xvZ2xpa19sYW1iZGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHlfQkNUX2xvZz1sb2coeSksIHlfQkNUX3NxcnQ9c3FydCh5KSwgLmFmdGVyPSJzYWxlcyIpCmBgYAoKQ29uc2lkZXJpYW1vIGkgZ3JhZmljaSAoKnNjYXR0ZXIgZSBsaW5lIHBsb3QqKSBkZWxsZSB0cmFzZm9ybWF0ZS4KYGBge3J9CiMgV2UgY29uc2lkZXIgdGhlIHNjYXR0ZXIgYW5kIGxpbmUgcGxvdHMgb2YgdGhlIHRyYW5zZm9ybWF0aW9ucwojIFRoZSBzY2F0dGVyIHBsb3RzCkRhdGFfZGYgPC0gQ2FtcnlfVHJuU19kZgpsZW5ndGggPC0gbnJvdyhEYXRhX2RmKQpGaXJzdF9EYXRlIDwtIHBhc3RlKERhdGFfZGYkTW9udGhbMV0sRGF0YV9kZiRZZWFyWzFdKQpMYXN0X0RhdGUgPC0gcGFzdGUoRGF0YV9kZiRNb250aFtsZW5ndGhdLERhdGFfZGYkWWVhcltsZW5ndGhdKQp0aXRsZV9jb250ZW50IDwtIGJxdW90ZShhdG9wKCJTY2F0dGVyIFBsb3Qgb2YgRm91ciBCb3gtQ294IFRyYW5zZm9ybWF0aW9ucyBvZiBUb3lvdGEgQ2Ftcnkgc2FsZXMgaW4gdGhlIFUuUy4gZnJvbSAiLCAuKEZpcnN0X0RhdGUpLCAiIHRvICIsIC4oTGFzdF9EYXRlKSkpCmNhcHRpb25fY29udGVudCA8LSAiQXV0aG9yOiBNYXR0ZW8gQ2hpYWNjaGlhIgpzdWJ0aXRsZV9jb250ZW50IDwtIGJxdW90ZShwYXN0ZSgiQkNUIEd1ZXJyZXJvIE1ldGhvZCAtIERhdGEgc2V0IHNpemUgIiwgLihsZW5ndGgpLH5+InNhbXBsZSBwb2ludHMiKSkKeF9uYW1lIDwtIGJxdW90ZSgiIikKIyBwcmltZUZhY3RvcnMobGVuZ3RoKQojIHByaW1lRmFjdG9ycyhsZW5ndGgtMSkKeF9icmVha3NfbnVtIDwtIDM5CnhfYnJlYWtzX21pbiA8LSBEYXRhX2RmJHRbMV0KeF9icmVha3NfbWF4IDwtIERhdGFfZGYkdFtsZW5ndGhdCnhfYmlud2lkdGggPC0gZmxvb3IoKHhfYnJlYWtzX21heC14X2JyZWFrc19taW4pL3hfYnJlYWtzX251bSkKeF9icmVha3MgPC0gc2VxKGZyb209eF9icmVha3NfbWluLCB0bz14X2JyZWFrc19tYXgsIGJ5PXhfYmlud2lkdGgpCmlmKCh4X2JyZWFrc19tYXgtbWF4KHhfYnJlYWtzKSk+eF9iaW53aWR0aC8yKXt4X2JyZWFrcyA8LSBjKHhfYnJlYWtzLHhfYnJlYWtzX21heCl9CiMgeF9sYWJzIDwtIGZvcm1hdCh4X2JyZWFrcywgc2NpZW50aWZpYz1GQUxTRSkKeF9sYWJzIDwtIERhdGFfZGYkRGF0YVt4X2JyZWFrc10KSiA8LSAwCnhfbGltcyA8LSBjKHhfYnJlYWtzX21pbi1KKnhfYmlud2lkdGgsIHhfYnJlYWtzX21heCtKKnhfYmlud2lkdGgpCmNvbF8xIDwtIGJxdW90ZSgiQlRDIEd1ZXJyZXJvIG1ldGhvZCIpCmNvbF8yIDwtIGJxdW90ZSgiQlRDIGxvZ2xpay4gbWV0aG9kIikKY29sXzMgPC0gYnF1b3RlKCJMb2cgdHJhbnNmb3JtLiIpCmNvbF80IDwtIGJxdW90ZSgiU3FydCB0cmFuc2Zvcm0uIikKY29sXzUgPC0gYnF1b3RlKCJSZWdyZXNzaW9uIGxpbmUiKQpjb2xfNiA8LSBicXVvdGUoImxvZXNzIikKbGVnX2xhYnMgPC0gYyhjb2xfMSwgY29sXzIsIGNvbF8zLCBjb2xfNCwgY29sXzUsIGNvbF82KQpsZWdfY29scyA8LSBjKCJjb2xfMSI9InJlZCIsICJjb2xfMiI9ImdyZWVuIiwgImNvbF8zIj0iYmx1ZSIsICJjb2xfNCI9Im1hZ2VudGEiLCAiY29sXzUiPSJibGFjayIsICJjb2xfNiI9ImJyb3duIikKbGVnX2JyZWFrcyA8LSBjKCJjb2xfMSIsICJjb2xfMiIsICJjb2xfMyIsICJjb2xfNCIsICJjb2xfNSIsICJjb2xfNiIpCiMgQm94LUNveCB0cmFuc2Zvcm1hdGlvbi1HdWVycmVybyBtZXRob2QKc3VidGl0bGVfY29udGVudCA8LSBicXVvdGUocGFzdGUoIkJDVCBHdWVycmVybyBNZXRob2QgLSBEYXRhIHNldCBzaXplICIsIC4obGVuZ3RoKSx+fiJzYW1wbGUgcG9pbnRzIikpCnlfbmFtZSA8LSBicXVvdGUoIk1vbi4gRGVhdGhzIHBlciAxMCwwMDAgKEJDVC1HdWVyci4gVHIuKSIpCnlfYnJlYWtzX251bSA8LSAyCnlfbWF4IDwtIG1heChEYXRhX2RmJHlfQkNUX0d1ZXJyKQp5X21pbiA8LSBtaW4oRGF0YV9kZiR5X0JDVF9HdWVycikKeV9iaW53aWR0aCA8LSByb3VuZCgoeV9tYXgteV9taW4pL3lfYnJlYWtzX251bSwgZGlnaXRzPTMpCnlfYnJlYWtzX2xvdyA8LSBjZWlsaW5nKHlfbWluL3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3NfdXAgPC0gY2VpbGluZyh5X21heC95X2JpbndpZHRoKSp5X2JpbndpZHRoCnlfYnJlYWtzIDwtIHJvdW5kKHNlcShmcm9tPXlfYnJlYWtzX2xvdywgdG89eV9icmVha3NfdXAsIGJ5PXlfYmlud2lkdGgpLGRpZ2l0cz0zKQp5X2xhYnMgPC0gZm9ybWF0KHlfYnJlYWtzLCBzY2llbnRpZmljPUZBTFNFKQpLIDwtIDAuNQp5X2xpbXMgPC0gYygoeV9icmVha3NfbG93LUsqeV9iaW53aWR0aCksICh5X2JyZWFrc191cCtLKnlfYmlud2lkdGgpKQp5X0JDVF9HdWVycl9zcCA8LSBnZ3Bsb3QoRGF0YV9kZiwgYWVzKHg9dCkpKwogIGdlb21fcG9pbnQoYWxwaGE9MSwgc2l6ZT0xLjUsIHNoYXBlPTE5LCBhZXMoeT15X0JDVF9HdWVyciwgY29sb3I9ImNvbF8xIiksIHNob3cubGVnZW5kPUZBTFNFKSArCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MSwgbGluZXdpZHRoPTAuOCwgbGluZXR5cGU9InNvbGlkIiwgYWVzKHg9dCwgeT15X0JDVF9HdWVyciwgY29sb3I9ImNvbF81IiksCiAgICAgICAgICAgICAgbWV0aG9kPSJsbSIgLCBmb3JtdWxhPXkgfiB4LCBzZT1GQUxTRSwgZnVsbHJhbmdlPUZBTFNFLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIGdlb21fc21vb3RoKGFscGhhPTEsIGxpbmV3aWR0aD0xLjAsIGxpbmV0eXBlPSJkYXNoZWQiLCBhZXMoeD10LCB5PXlfQkNUX0d1ZXJyLCBjb2xvcj0iY29sXzYiKSwgCiAgICAgICAgICAgICAgbWV0aG9kPSJsb2VzcyIsIGZvcm11bGE9eSB+IHgsIHNlPUZBTFNFLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lPXhfbmFtZSwgYnJlYWtzPXhfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXhfbGltcykgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lPXlfbmFtZSwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXlfbGltcywKICAgICAgICAgICAgICAgICAgICAgc2VjLmF4aXM9IHNlY19heGlzKH4uLCBicmVha3M9eV9icmVha3MsIGxhYmVscz15X2xhYnMpKSArCiAgIyAgZ2d0aXRsZSh0aXRsZV9jb250ZW50KSArCiAgbGFicyhzdWJ0aXRsZT1zdWJ0aXRsZV9jb250ZW50KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkxlZ2VuZCIsIGxhYmVscz1sZWdfbGFicywgdmFsdWVzPWxlZ19jb2xzKSArCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MC41LCBzaXplPTE4KSwgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MCksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPS00NSwgdmp1c3Q9MSwgaGp1c3Q9LTAuMyksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aD11bml0KDAuOCwiY20iKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpwbG90KHlfQkNUX0d1ZXJyX3NwKQojIEJveC1Db3ggdHJhbnNmb3JtYXRpb24tbG9nLWxpa2VsaWhvb2QgbWV0aG9kIApzdWJ0aXRsZV9jb250ZW50IDwtIGJxdW90ZShwYXN0ZSgiQkNUIExvZy1saWtlbGlob29kIE1ldGhvZCAtIERhdGEgc2V0IHNpemUgIiwgLihsZW5ndGgpLH5+InNhbXBsZSBwb2ludHMiKSkKeV9uYW1lIDwtIGJxdW90ZSgiTW9uLiBEZWF0aHMgcGVyIDEwLDAwMCAoQkNULUxvZ2xpay4gVHIuKSIpCnlfYnJlYWtzX251bSA8LSAyCnlfbWF4IDwtIG1heChEYXRhX2RmJHlfQkNUX2xvZ2xpaykKeV9taW4gPC0gbWluKERhdGFfZGYkeV9CQ1RfbG9nbGlrKQp5X2JpbndpZHRoIDwtIHJvdW5kKCh5X21heC15X21pbikveV9icmVha3NfbnVtLCBkaWdpdHM9MykKeV9icmVha3NfbG93IDwtIGNlaWxpbmcoeV9taW4veV9iaW53aWR0aCkqeV9iaW53aWR0aAp5X2JyZWFrc191cCA8LSBjZWlsaW5nKHlfbWF4L3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3MgPC0gcm91bmQoc2VxKGZyb209eV9icmVha3NfbG93LCB0bz15X2JyZWFrc191cCwgYnk9eV9iaW53aWR0aCksZGlnaXRzPTMpCnlfbGFicyA8LSBmb3JtYXQoeV9icmVha3MsIHNjaWVudGlmaWM9RkFMU0UpCksgPC0gMC41CnlfbGltcyA8LSBjKCh5X2JyZWFrc19sb3ctSyp5X2JpbndpZHRoKSwgKHlfYnJlYWtzX3VwK0sqeV9iaW53aWR0aCkpCnlfQkNUX2xvZ2xpa19zcCA8LSBnZ3Bsb3QoRGF0YV9kZiwgYWVzKHg9dCkpKwogIGdlb21fcG9pbnQoYWxwaGE9MSwgc2l6ZT0xLjUsIHNoYXBlPTE5LCBhZXMoeT15X0JDVF9sb2dsaWssIGNvbG9yPSJjb2xfMiIpLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIGdlb21fc21vb3RoKGFscGhhPTEsIGxpbmV3aWR0aD0wLjgsIGxpbmV0eXBlPSJzb2xpZCIsIGFlcyh4PXQsIHk9eV9CQ1RfbG9nbGlrLCBjb2xvcj0iY29sXzUiKSwKICAgICAgICAgICAgICBtZXRob2Q9ImxtIiAsIGZvcm11bGE9eSB+IHgsIHNlPUZBTFNFLCBmdWxscmFuZ2U9RkFMU0UsIHNob3cubGVnZW5kPUZBTFNFKSArCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MSwgbGluZXdpZHRoPTEuMCwgbGluZXR5cGU9ImRhc2hlZCIsIGFlcyh4PXQsIHk9eV9CQ1RfbG9nbGlrLCBjb2xvcj0iY29sXzYiKSwgCiAgICAgICAgICAgICAgbWV0aG9kPSJsb2VzcyIsIGZvcm11bGE9eSB+IHgsIHNlPUZBTFNFLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lPXhfbmFtZSwgYnJlYWtzPXhfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXhfbGltcykgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lPXlfbmFtZSwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXlfbGltcywKICAgICAgICAgICAgICAgICAgICAgc2VjLmF4aXM9IHNlY19heGlzKH4uLCBicmVha3M9eV9icmVha3MsIGxhYmVscz15X2xhYnMpKSArCiAgbGFicyhzdWJ0aXRsZT1zdWJ0aXRsZV9jb250ZW50KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkxlZ2VuZCIsIGxhYmVscz1sZWdfbGFicywgdmFsdWVzPWxlZ19jb2xzKSArCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MC41LCBzaXplPTE4KSwgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MCksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT0tNDUsIHZqdXN0PTEsIGhqdXN0PS0wLjMpLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGg9dW5pdCgwLjgsImNtIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKcGxvdCh5X0JDVF9sb2dsaWtfc3ApCiMgTG9nYXJpdGhtIHRyYW5zZm9ybWF0aW9uCnN1YnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKHBhc3RlKCJMb2dhcml0aG0gVHJhbnNmb3JtYXRpb24gLSBEYXRhIHNldCBzaXplICIsIC4obGVuZ3RoKSx+fiJzYW1wbGUgcG9pbnRzIikpCnlfbmFtZSA8LSBicXVvdGUoIk1vbi4gRGVhdGhzIHBlciAxMCwwMDAgKExvZy4gVHIuKSIpCnlfYnJlYWtzX251bSA8LSAyCnlfbWF4IDwtIG1heChEYXRhX2RmJHlfQkNUX2xvZykKeV9taW4gPC0gbWluKERhdGFfZGYkeV9CQ1RfbG9nKQp5X2JpbndpZHRoIDwtIHJvdW5kKCh5X21heC15X21pbikveV9icmVha3NfbnVtLCBkaWdpdHM9MykKeV9icmVha3NfbG93IDwtIGNlaWxpbmcoeV9taW4veV9iaW53aWR0aCkqeV9iaW53aWR0aAp5X2JyZWFrc191cCA8LSBjZWlsaW5nKHlfbWF4L3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3MgPC0gcm91bmQoc2VxKGZyb209eV9icmVha3NfbG93LCB0bz15X2JyZWFrc191cCwgYnk9eV9iaW53aWR0aCksZGlnaXRzPTMpCnlfbGFicyA8LSBmb3JtYXQoeV9icmVha3MsIHNjaWVudGlmaWM9RkFMU0UpCksgPC0gMC41CnlfbGltcyA8LSBjKCh5X2JyZWFrc19sb3ctSyp5X2JpbndpZHRoKSwgKHlfYnJlYWtzX3VwK0sqeV9iaW53aWR0aCkpCnlfQkNUX2xvZ19zcCA8LSBnZ3Bsb3QoRGF0YV9kZiwgYWVzKHg9dCkpICsKICBnZW9tX3BvaW50KGFscGhhPTEsIHNpemU9MS41LCBzaGFwZT0xOSwgYWVzKHk9eV9CQ1RfbG9nLCBjb2xvcj0iY29sXzMiKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsKICBnZW9tX3Ntb290aChhbHBoYT0xLCBsaW5ld2lkdGg9MC44LCBsaW5ldHlwZT0ic29saWQiLCBhZXMoeD10LCB5PXlfQkNUX2xvZywgY29sb3I9ImNvbF81IiksCiAgICAgICAgICAgICAgbWV0aG9kPSJsbSIgLCBmb3JtdWxhPXkgfiB4LCBzZT1GQUxTRSwgZnVsbHJhbmdlPUZBTFNFLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIGdlb21fc21vb3RoKGFscGhhPTEsIGxpbmV3aWR0aD0xLjAsIGxpbmV0eXBlPSJkYXNoZWQiLCBhZXMoeD10LCB5PXlfQkNUX2xvZywgY29sb3I9ImNvbF82IiksIAogICAgICAgICAgICAgIG1ldGhvZD0ibG9lc3MiLCBmb3JtdWxhPXkgfiB4LCBzZT1GQUxTRSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZT14X25hbWUsIGJyZWFrcz14X2JyZWFrcywgbGFiZWxzPXhfbGFicywgbGltaXRzPXhfbGltcykgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lPXlfbmFtZSwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXlfbGltcywKICAgICAgICAgICAgICAgICAgICAgc2VjLmF4aXM9IHNlY19heGlzKH4uLCBicmVha3M9eV9icmVha3MsIGxhYmVscz15X2xhYnMpKSArCiAgbGFicyhzdWJ0aXRsZT1zdWJ0aXRsZV9jb250ZW50KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkxlZ2VuZCIsIGxhYmVscz1sZWdfbGFicywgdmFsdWVzPWxlZ19jb2xzKSArCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MC41LCBzaXplPTE4KSwgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MCksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT0tNDUsIHZqdXN0PTEsIGhqdXN0PS0wLjMpLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGg9dW5pdCgwLjgsImNtIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKcGxvdCh5X0JDVF9sb2dfc3ApCiMgU3F1YXJlIFJvb3QgdHJhbnNmb3JtYXRpb24Kc3VidGl0bGVfY29udGVudCA8LSBicXVvdGUocGFzdGUoIlNxdWFyZSBSb290IFRyYW5zZm9ybWF0aW9uIC0gRGF0YSBzZXQgc2l6ZSAiLCAuKGxlbmd0aCksfn4ic2FtcGxlIHBvaW50cyIpKQp5X25hbWUgPC0gYnF1b3RlKCJNb24uIERlYXRocyBwZXIgMTAsMDAwIChTcXJ0IFRyLikiKQp5X2JyZWFrc19udW0gPC0gMgp5X21heCA8LSBtYXgoRGF0YV9kZiR5X0JDVF9zcXJ0KQp5X21pbiA8LSBtaW4oRGF0YV9kZiR5X0JDVF9zcXJ0KQp5X2JpbndpZHRoIDwtIHJvdW5kKCh5X21heC15X21pbikveV9icmVha3NfbnVtLCBkaWdpdHM9MykKeV9icmVha3NfbG93IDwtIGZsb29yKHlfbWluL3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3NfdXAgPC0gY2VpbGluZyh5X21heC95X2JpbndpZHRoKSp5X2JpbndpZHRoCnlfYnJlYWtzIDwtIHJvdW5kKHNlcShmcm9tPXlfYnJlYWtzX2xvdywgdG89eV9icmVha3NfdXAsIGJ5PXlfYmlud2lkdGgpLGRpZ2l0cz0zKQp5X2xhYnMgPC0gZm9ybWF0KHlfYnJlYWtzLCBzY2llbnRpZmljPUZBTFNFKQpLIDwtIDAuMAp5X2xpbXMgPC0gYygoeV9icmVha3NfbG93LUsqeV9iaW53aWR0aCksICh5X2JyZWFrc191cCtLKnlfYmlud2lkdGgpKQp5X0JDVF9zcXJ0X3NwIDwtIGdncGxvdChEYXRhX2RmLCBhZXMoeD10KSkrCiAgZ2VvbV9wb2ludChhbHBoYT0xLCBzaXplPTEuNSwgc2hhcGU9MTksIGFlcyh5PXlfQkNUX3NxcnQsIGNvbG9yPSJjb2xfNCIpKSArCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MSwgbGluZXdpZHRoPTAuOCwgbGluZXR5cGU9InNvbGlkIiwgYWVzKHg9dCwgeT15X0JDVF9zcXJ0LCBjb2xvcj0iY29sXzUiKSwKICAgICAgICAgICAgICBtZXRob2Q9ImxtIiAsIGZvcm11bGE9eSB+IHgsIHNlPUZBTFNFLCBmdWxscmFuZ2U9RkFMU0UsIHNob3cubGVnZW5kPUZBTFNFKSArCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MSwgbGluZXdpZHRoPTEuMCwgbGluZXR5cGU9ImRhc2hlZCIsIGFlcyh4PXQsIHk9eV9CQ1Rfc3FydCwgY29sb3I9ImNvbF82IiksIAogICAgICAgICAgICAgIG1ldGhvZD0ibG9lc3MiLCBmb3JtdWxhPXkgfiB4LCBzZT1GQUxTRSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZT14X25hbWUsIGJyZWFrcz14X2JyZWFrcywgbGFiZWxzPXhfbGFicywgbGltaXRzPXhfbGltcykgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lPXlfbmFtZSwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXlfbGltcywKICAgICAgICAgICAgICAgICAgICAgc2VjLmF4aXM9IHNlY19heGlzKH4uLCBicmVha3M9eV9icmVha3MsIGxhYmVscz15X2xhYnMpKSArCiAgbGFicyhzdWJ0aXRsZT1zdWJ0aXRsZV9jb250ZW50KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkxlZ2VuZCIsIGxhYmVscz1sZWdfbGFicywgdmFsdWVzPWxlZ19jb2xzKSArCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQobnJvdz0xKSkgKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xOCksIHBsb3Quc3VidGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTApLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9LTQ1LCB2anVzdD0xLCBoanVzdD0tMC4zKSwKICAgICAgICBsZWdlbmQua2V5LndpZHRoPXVuaXQoMC44LCJjbSIpLCBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpCnBsb3QoeV9CQ1Rfc3FydF9zcCkKCmdfbGVnZW5kIDwtIGZ1bmN0aW9uKGEuZ3Bsb3QpewogIHRtcCA8LSBnZ3Bsb3RfZ3RhYmxlKGdncGxvdF9idWlsZChhLmdwbG90KSkKICBsZWcgPC0gd2hpY2goc2FwcGx5KHRtcCRncm9icywgZnVuY3Rpb24oeCkgeCRuYW1lKSA9PSAiZ3VpZGUtYm94IikKICBsZWdlbmQgPC0gdG1wJGdyb2JzW1tsZWddXQogIHJldHVybihsZWdlbmQpfQpteWxlZ2VuZCA8LSBnX2xlZ2VuZCh5X0JDVF9zcXJ0X3NwKQojCmdyaWQuYXJyYW5nZShhcnJhbmdlR3JvYih5X0JDVF9HdWVycl9zcCt0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHlfQkNUX2xvZ2xpa19zcCt0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHlfQkNUX2xvZ19zcCt0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHlfQkNUX3NxcnRfc3ArdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBucm93PTIpLAogICAgICAgICAgICAgdG9wPXRleHRHcm9iKHRpdGxlX2NvbnRlbnQsIGdwPWdwYXIoZm9udGZhY2U9MSwgY2V4PTEuMSkpLAogICAgICAgICAgICAgYm90dG9tPXRleHRHcm9iKGNhcHRpb25fY29udGVudCwgZ3A9Z3Bhcihmb250ZmFjZT0zLCBmb250c2l6ZT05KSwgaGp1c3Q9MSwgeD0xKSwKICAgICAgICAgICAgIG15bGVnZW5kLCBucm93PTIsIGhlaWdodHM9YygxMCwgMSkpCmBgYApgYGB7cn0KdGl0bGVfY29udGVudCA8LSBicXVvdGUoYXRvcCgiTGluZSBQbG90IG9mIEZvdXIgQm94LUNveCBUcmFuc2Zvcm1hdGlvbnMgb2YgVG95b3RhIENhbXJ5IHNhbGVzIGluIHRoZSBVLlMuIGZyb20gIiwgLihGaXJzdF9EYXRlKSwgIiB0byAiLCAuKExhc3RfRGF0ZSkpKQoKIyBCb3gtQ294IHRyYW5zZm9ybWF0aW9uLUd1ZXJyZXJvIG1ldGhvZApzdWJ0aXRsZV9jb250ZW50IDwtIGJxdW90ZShwYXN0ZSgiQkNUIEd1ZXJyZXJvIE1ldGhvZCAtIERhdGEgc2V0IHNpemUgIiwgLihsZW5ndGgpLH5+InNhbXBsZSBwb2ludHMiKSkKeV9uYW1lIDwtIGJxdW90ZSgiTW9uLiBEZWF0aHMgcGVyIDEwLDAwMCAoQkNULUd1ZXJyLiBUci4pIikKeV9icmVha3NfbnVtIDwtIDIKeV9tYXggPC0gbWF4KERhdGFfZGYkeV9CQ1RfR3VlcnIpCnlfbWluIDwtIG1pbihEYXRhX2RmJHlfQkNUX0d1ZXJyKQp5X2JpbndpZHRoIDwtIHJvdW5kKCh5X21heC15X21pbikveV9icmVha3NfbnVtLCBkaWdpdHM9MykKeV9icmVha3NfbG93IDwtIGNlaWxpbmcoeV9taW4veV9iaW53aWR0aCkqeV9iaW53aWR0aAp5X2JyZWFrc191cCA8LSBjZWlsaW5nKHlfbWF4L3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3MgPC0gcm91bmQoc2VxKGZyb209eV9icmVha3NfbG93LCB0bz15X2JyZWFrc191cCwgYnk9eV9iaW53aWR0aCksZGlnaXRzPTMpCnlfbGFicyA8LSBmb3JtYXQoeV9icmVha3MsIHNjaWVudGlmaWM9RkFMU0UpCksgPC0gMC41CnlfbGltcyA8LSBjKCh5X2JyZWFrc19sb3ctSyp5X2JpbndpZHRoKSwgKHlfYnJlYWtzX3VwK0sqeV9iaW53aWR0aCkpCnlfQkNUX0d1ZXJyX2xwIDwtIGdncGxvdChEYXRhX2RmLCBhZXMoeD10KSkrCiAgZ2VvbV9saW5lKGFscGhhPTEsIGxpbmV3aWR0aD0wLjgsIGxpbmV0eXBlPSJzb2xpZCIsIGFlcyh4PXQsIHk9eV9CQ1RfR3VlcnIsIGNvbG9yPSJjb2xfMSIsIGdyb3VwPTEpLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIGdlb21fc21vb3RoKGFscGhhPTEsIGxpbmV3aWR0aD0wLjgsIGxpbmV0eXBlPSJzb2xpZCIsIGFlcyh4PXQsIHk9eV9CQ1RfR3VlcnIsIGNvbG9yPSJjb2xfNSIpLAogICAgICAgICAgICAgIG1ldGhvZD0ibG0iICwgZm9ybXVsYT15IH4geCwgc2U9RkFMU0UsIGZ1bGxyYW5nZT1GQUxTRSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsKICBnZW9tX3Ntb290aChhbHBoYT0xLCBsaW5ld2lkdGg9MS4wLCBsaW5ldHlwZT0iZGFzaGVkIiwgYWVzKHg9dCwgeT15X0JDVF9HdWVyciwgY29sb3I9ImNvbF82IiksIAogICAgICAgICAgICAgIG1ldGhvZD0ibG9lc3MiLCBmb3JtdWxhPXkgfiB4LCBzZT1GQUxTRSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZT14X25hbWUsIGJyZWFrcz14X2JyZWFrcywgbGFiZWxzPU5VTEwsIGxpbWl0cz14X2xpbXMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZT15X25hbWUsIGJyZWFrcz15X2JyZWFrcywgbGFiZWxzPU5VTEwsIGxpbWl0cz15X2xpbXMsCiAgICAgICAgICAgICAgICAgICAgIHNlYy5heGlzPSBzZWNfYXhpcyh+LiwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9eV9sYWJzKSkgKwogICMgIGdndGl0bGUodGl0bGVfY29udGVudCkgKwogIGxhYnMoc3VidGl0bGU9c3VidGl0bGVfY29udGVudCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJMZWdlbmQiLCBsYWJlbHM9bGVnX2xhYnMsIHZhbHVlcz1sZWdfY29scykgKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSksIHBsb3Quc3VidGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTApLAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT0tNDUsIHZqdXN0PTEsIGhqdXN0PS0wLjMpLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGg9dW5pdCgwLjgsImNtIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKcGxvdCh5X0JDVF9HdWVycl9scCkKIyBCb3gtQ294IHRyYW5zZm9ybWF0aW9uLWxvZy1saWtlbGlob29kIG1ldGhvZCAKc3VidGl0bGVfY29udGVudCA8LSBicXVvdGUocGFzdGUoIkJDVCBMb2ctbGlrZWxpaG9vZCBNZXRob2QgLSBEYXRhIHNldCBzaXplICIsIC4obGVuZ3RoKSx+fiJzYW1wbGUgcG9pbnRzIikpCnlfbmFtZSA8LSBicXVvdGUoIk1vbi4gRGVhdGhzIHBlciAxMCwwMDAgKEJDVC1Mb2dsaWsuIFRyLikiKQp5X2JyZWFrc19udW0gPC0gMgp5X21heCA8LSBtYXgoRGF0YV9kZiR5X0JDVF9sb2dsaWspCnlfbWluIDwtIG1pbihEYXRhX2RmJHlfQkNUX2xvZ2xpaykKeV9iaW53aWR0aCA8LSByb3VuZCgoeV9tYXgteV9taW4pL3lfYnJlYWtzX251bSwgZGlnaXRzPTMpCnlfYnJlYWtzX2xvdyA8LSBjZWlsaW5nKHlfbWluL3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3NfdXAgPC0gY2VpbGluZyh5X21heC95X2JpbndpZHRoKSp5X2JpbndpZHRoCnlfYnJlYWtzIDwtIHJvdW5kKHNlcShmcm9tPXlfYnJlYWtzX2xvdywgdG89eV9icmVha3NfdXAsIGJ5PXlfYmlud2lkdGgpLGRpZ2l0cz0zKQp5X2xhYnMgPC0gZm9ybWF0KHlfYnJlYWtzLCBzY2llbnRpZmljPUZBTFNFKQpLIDwtIDAuNQp5X2xpbXMgPC0gYygoeV9icmVha3NfbG93LUsqeV9iaW53aWR0aCksICh5X2JyZWFrc191cCtLKnlfYmlud2lkdGgpKQp5X0JDVF9sb2dsaWtfbHAgPC0gZ2dwbG90KERhdGFfZGYsIGFlcyh4PXQpKSsKICBnZW9tX2xpbmUoYWxwaGE9MSwgbGluZXdpZHRoPTAuOCwgbGluZXR5cGU9InNvbGlkIiwgYWVzKHg9dCwgeT15X0JDVF9sb2dsaWssIGNvbG9yPSJjb2xfMiIsIGdyb3VwPTEpLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIGdlb21fc21vb3RoKGFscGhhPTEsIGxpbmV3aWR0aD0wLjgsIGxpbmV0eXBlPSJzb2xpZCIsIGFlcyh4PXQsIHk9eV9CQ1RfbG9nbGlrLCBjb2xvcj0iY29sXzUiKSwKICAgICAgICAgICAgICBtZXRob2Q9ImxtIiAsIGZvcm11bGE9eSB+IHgsIHNlPUZBTFNFLCBmdWxscmFuZ2U9RkFMU0UsIHNob3cubGVnZW5kPUZBTFNFKSArCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MSwgbGluZXdpZHRoPTEuMCwgbGluZXR5cGU9ImRhc2hlZCIsIGFlcyh4PXQsIHk9eV9CQ1RfbG9nbGlrLCBjb2xvcj0iY29sXzYiKSwgCiAgICAgICAgICAgICAgbWV0aG9kPSJsb2VzcyIsIGZvcm11bGE9eSB+IHgsIHNlPUZBTFNFLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lPXhfbmFtZSwgYnJlYWtzPXhfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXhfbGltcykgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lPXlfbmFtZSwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXlfbGltcywKICAgICAgICAgICAgICAgICAgICAgc2VjLmF4aXM9IHNlY19heGlzKH4uLCBicmVha3M9eV9icmVha3MsIGxhYmVscz15X2xhYnMpKSArCiAgbGFicyhzdWJ0aXRsZT1zdWJ0aXRsZV9jb250ZW50KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkxlZ2VuZCIsIGxhYmVscz1sZWdfbGFicywgdmFsdWVzPWxlZ19jb2xzKSArCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MC41KSwgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MCksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT0tNDUsIHZqdXN0PTEsIGhqdXN0PS0wLjMpLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGg9dW5pdCgwLjgsImNtIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKcGxvdCh5X0JDVF9sb2dsaWtfbHApCiMgTG9nYXJpdGhtIHRyYW5zZm9ybWF0aW9uCnN1YnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKHBhc3RlKCJMb2dhcml0aG0gVHJhbnNmb3JtYXRpb24gLSBEYXRhIHNldCBzaXplICIsIC4obGVuZ3RoKSx+fiJzYW1wbGUgcG9pbnRzIikpCnlfbmFtZSA8LSBicXVvdGUoIk1vbi4gRGVhdGhzIHBlciAxMCwwMDAgKExvZy4gVHIuKSIpCnlfYnJlYWtzX251bSA8LSAyCnlfbWF4IDwtIG1heChEYXRhX2RmJHlfQkNUX2xvZykKeV9taW4gPC0gbWluKERhdGFfZGYkeV9CQ1RfbG9nKQp5X2JpbndpZHRoIDwtIHJvdW5kKCh5X21heC15X21pbikveV9icmVha3NfbnVtLCBkaWdpdHM9MykKeV9icmVha3NfbG93IDwtIGNlaWxpbmcoeV9taW4veV9iaW53aWR0aCkqeV9iaW53aWR0aAp5X2JyZWFrc191cCA8LSBjZWlsaW5nKHlfbWF4L3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3MgPC0gcm91bmQoc2VxKGZyb209eV9icmVha3NfbG93LCB0bz15X2JyZWFrc191cCwgYnk9eV9iaW53aWR0aCksZGlnaXRzPTMpCnlfbGFicyA8LSBmb3JtYXQoeV9icmVha3MsIHNjaWVudGlmaWM9RkFMU0UpCksgPC0gMC41CnlfbGltcyA8LSBjKCh5X2JyZWFrc19sb3ctSyp5X2JpbndpZHRoKSwgKHlfYnJlYWtzX3VwK0sqeV9iaW53aWR0aCkpCnlfQkNUX2xvZ19scCA8LSBnZ3Bsb3QoRGF0YV9kZiwgYWVzKHg9dCkpICsKICBnZW9tX2xpbmUoYWxwaGE9MSwgbGluZXdpZHRoPTAuOCwgbGluZXR5cGU9InNvbGlkIiwgYWVzKHg9dCwgeT15X0JDVF9sb2csIGNvbG9yPSJjb2xfMyIsIGdyb3VwPTEpLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIGdlb21fc21vb3RoKGFscGhhPTEsIGxpbmV3aWR0aD0wLjgsIGxpbmV0eXBlPSJzb2xpZCIsIGFlcyh4PXQsIHk9eV9CQ1RfbG9nLCBjb2xvcj0iY29sXzUiKSwKICAgICAgICAgICAgICBtZXRob2Q9ImxtIiAsIGZvcm11bGE9eSB+IHgsIHNlPUZBTFNFLCBmdWxscmFuZ2U9RkFMU0UsIHNob3cubGVnZW5kPUZBTFNFKSArCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MSwgbGluZXdpZHRoPTEuMCwgbGluZXR5cGU9ImRhc2hlZCIsIGFlcyh4PXQsIHk9eV9CQ1RfbG9nLCBjb2xvcj0iY29sXzYiKSwgCiAgICAgICAgICAgICAgbWV0aG9kPSJsb2VzcyIsIGZvcm11bGE9eSB+IHgsIHNlPUZBTFNFLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lPXhfbmFtZSwgYnJlYWtzPXhfYnJlYWtzLCBsYWJlbHM9eF9sYWJzLCBsaW1pdHM9eF9saW1zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWU9eV9uYW1lLCBicmVha3M9eV9icmVha3MsIGxhYmVscz1OVUxMLCBsaW1pdHM9eV9saW1zLAogICAgICAgICAgICAgICAgICAgICBzZWMuYXhpcz0gc2VjX2F4aXMofi4sIGJyZWFrcz15X2JyZWFrcywgbGFiZWxzPXlfbGFicykpICsKICBsYWJzKHN1YnRpdGxlPXN1YnRpdGxlX2NvbnRlbnQpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iTGVnZW5kIiwgbGFiZWxzPWxlZ19sYWJzLCB2YWx1ZXM9bGVnX2NvbHMpICsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUpLCBwbG90LnN1YnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPS00NSwgdmp1c3Q9MSwgaGp1c3Q9LTAuMyksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aD11bml0KDAuOCwiY20iKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpwbG90KHlfQkNUX2xvZ19scCkKIyBTcXVhcmUgUm9vdCB0cmFuc2Zvcm1hdGlvbgpzdWJ0aXRsZV9jb250ZW50IDwtIGJxdW90ZShwYXN0ZSgiU3F1YXJlIFJvb3QgVHJhbnNmb3JtYXRpb24gLSBEYXRhIHNldCBzaXplICIsIC4obGVuZ3RoKSx+fiJzYW1wbGUgcG9pbnRzIikpCnlfbmFtZSA8LSBicXVvdGUoIk1vbi4gRGVhdGhzIHBlciAxMCwwMDAgKFNxcnQgVHIuKSIpCnlfYnJlYWtzX251bSA8LSAyCnlfbWF4IDwtIG1heChEYXRhX2RmJHlfQkNUX3NxcnQpCnlfbWluIDwtIG1pbihEYXRhX2RmJHlfQkNUX3NxcnQpCnlfYmlud2lkdGggPC0gcm91bmQoKHlfbWF4LXlfbWluKS95X2JyZWFrc19udW0sIGRpZ2l0cz0zKQp5X2JyZWFrc19sb3cgPC0gZmxvb3IoeV9taW4veV9iaW53aWR0aCkqeV9iaW53aWR0aAp5X2JyZWFrc191cCA8LSBjZWlsaW5nKHlfbWF4L3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3MgPC0gcm91bmQoc2VxKGZyb209eV9icmVha3NfbG93LCB0bz15X2JyZWFrc191cCwgYnk9eV9iaW53aWR0aCksZGlnaXRzPTMpCnlfbGFicyA8LSBmb3JtYXQoeV9icmVha3MsIHNjaWVudGlmaWM9RkFMU0UpCksgPC0gMC4wCnlfbGltcyA8LSBjKCh5X2JyZWFrc19sb3ctSyp5X2JpbndpZHRoKSwgKHlfYnJlYWtzX3VwK0sqeV9iaW53aWR0aCkpCnlfQkNUX3NxcnRfbHAgPC0gZ2dwbG90KERhdGFfZGYsIGFlcyh4PXQpKSsKICBnZW9tX2xpbmUoYWxwaGE9MSwgbGluZXdpZHRoPTAuOCwgbGluZXR5cGU9InNvbGlkIiwgYWVzKHg9dCwgeT15X0JDVF9zcXJ0LCBjb2xvcj0iY29sXzQiLCBncm91cD0xKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsKICBnZW9tX3Ntb290aChhbHBoYT0xLCBsaW5ld2lkdGg9MC44LCBsaW5ldHlwZT0ic29saWQiLCBhZXMoeD10LCB5PXlfQkNUX3NxcnQsIGNvbG9yPSJjb2xfNSIpLAogICAgICAgICAgICAgIG1ldGhvZD0ibG0iICwgZm9ybXVsYT15IH4geCwgc2U9RkFMU0UsIGZ1bGxyYW5nZT1GQUxTRSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsKICBnZW9tX3Ntb290aChhbHBoYT0xLCBsaW5ld2lkdGg9MS4wLCBsaW5ldHlwZT0iZGFzaGVkIiwgYWVzKHg9dCwgeT15X0JDVF9zcXJ0LCBjb2xvcj0iY29sXzYiKSwgCiAgICAgICAgICAgICAgbWV0aG9kPSJsb2VzcyIsIGZvcm11bGE9eSB+IHgsIHNlPUZBTFNFLCBzaG93LmxlZ2VuZD1GQUxTRSkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lPXhfbmFtZSwgYnJlYWtzPXhfYnJlYWtzLCBsYWJlbHM9eF9sYWJzLCBsaW1pdHM9eF9saW1zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWU9eV9uYW1lLCBicmVha3M9eV9icmVha3MsIGxhYmVscz1OVUxMLCBsaW1pdHM9eV9saW1zLAogICAgICAgICAgICAgICAgICAgICBzZWMuYXhpcz0gc2VjX2F4aXMofi4sIGJyZWFrcz15X2JyZWFrcywgbGFiZWxzPXlfbGFicykpICsKICBsYWJzKHN1YnRpdGxlPXN1YnRpdGxlX2NvbnRlbnQpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iTGVnZW5kIiwgbGFiZWxzPWxlZ19sYWJzLCB2YWx1ZXM9bGVnX2NvbHMpICsKICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZChucm93PTEpKSArCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MC41KSwgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MCksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT0tNDUsIHZqdXN0PTEsIGhqdXN0PS0wLjMpLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGg9dW5pdCgwLjgsImNtIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKcGxvdCh5X0JDVF9zcXJ0X2xwKQpncmlkLmFycmFuZ2UoYXJyYW5nZUdyb2IoeV9CQ1RfR3VlcnJfbHArdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksCiAgICAgICAgICAgICAgICAgICAgICAgICB5X0JDVF9sb2dsaWtfbHArdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksCiAgICAgICAgICAgICAgICAgICAgICAgICB5X0JDVF9sb2dfbHArdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksCiAgICAgICAgICAgICAgICAgICAgICAgICB5X0JDVF9zcXJ0X2xwK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgbnJvdz0yKSwKICAgICAgICAgICAgIHRvcD10ZXh0R3JvYih0aXRsZV9jb250ZW50LCBncD1ncGFyKGZvbnRmYWNlPTEsIGNleD0xLjEpKSwKICAgICAgICAgICAgIGJvdHRvbT10ZXh0R3JvYihjYXB0aW9uX2NvbnRlbnQsIGdwPWdwYXIoZm9udGZhY2U9MywgZm9udHNpemU9OSksIGhqdXN0PTEsIHg9MSksCiAgICAgICAgICAgICBteWxlZ2VuZCwgbnJvdz0yLCBoZWlnaHRzPWMoMTAsIDEpKQpgYGAKUGVyIGlsIHJlc3RvIGRlbGwnYW5hbGlzaSBzaSB1dGlsaXp6ZXLDoCBsYSB0cmFzZm9ybWF0YSBjaGUgcmVzdGl0dWlzY2UgaSBtaWdsaW9yaSB2YWxvcmkgaW4gdGVybWluaSBkaSBvbW9zY2hlZGFzdGljaXTDoC4KCioqR3VlcnJlcm8gTWV0aG9kKio6CmBgYHtyfQp5X0JDVF9HdWVycl9zdHVkX0JQIDwtIGxtdGVzdDo6YnB0ZXN0KGZvcm11bGE9eV9CQ1RfR3VlcnJ+dCwgdmFyZm9ybXVsYT1OVUxMLCBzdHVkZW50aXplPUZBTFNFLCBkYXRhPURhdGFfZGYpCnNob3coeV9CQ1RfR3VlcnJfc3R1ZF9CUCkKCiMgVGhlIHVuc3R1ZGVudGl6ZWQgV2hpdGUgdGVzdAp5X0JDVF9HdWVycl9zdHVkX1cgPC0gbG10ZXN0OjpicHRlc3QoZm9ybXVsYT15X0JDVF9HdWVycn50LCB2YXJmb3JtdWxhPXlfQkNUX0d1ZXJyfnQrSSh0XjIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0dWRlbnRpemU9RkFMU0UsIGRhdGE9RGF0YV9kZikKc2hvdyh5X0JDVF9HdWVycl9zdHVkX1cpCmBgYAoqKkxvZy1MaWsgTWV0aG9kKio6CmBgYHtyfQojIFRoZSB1bnN0dWRlbnRpemVkIEJyZXVzY2gtUGFnYW4gdGVzdAp5X0JDVF9sb2dsaWtfc3R1ZF9CUCA8LSBsbXRlc3Q6OmJwdGVzdChmb3JtdWxhPXlfQkNUX2xvZ2xpa350LCB2YXJmb3JtdWxhPU5VTEwsIHN0dWRlbnRpemU9RkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPURhdGFfZGYpCnNob3coeV9CQ1RfbG9nbGlrX3N0dWRfQlApCgojIFRoZSB1bnN0dWRlbnRpemVkIFdoaXRlIHRlc3QKeV9CQ1RfbG9nbGlrX3N0dWRfVyA8LSBsbXRlc3Q6OmJwdGVzdChmb3JtdWxhPXlfQkNUX2xvZ2xpa350LCB2YXJmb3JtdWxhPXlfQkNUX2xvZ2xpa350K0kodF4yKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3R1ZGVudGl6ZT1GQUxTRSwgZGF0YT1EYXRhX2RmKQpzaG93KHlfQkNUX2xvZ2xpa19zdHVkX1cpCmBgYAoqKkxvZyBNZXRob2QqKjoKYGBge3J9CiMgVGhlIHN0dWRlbnRpemVkIEJyZXVzY2gtUGFnYW4gdGVzdAp5X0JDVF9sb2dfc3R1ZF9CUCA8LSBsbXRlc3Q6OmJwdGVzdChmb3JtdWxhPXlfQkNUX2xvZ350LCB2YXJmb3JtdWxhPU5VTEwsIHN0dWRlbnRpemU9VFJVRSwgZGF0YT1EYXRhX2RmKQpzaG93KHlfQkNUX2xvZ19zdHVkX0JQKQoKIwojIFRoZSBzdHVkZW50aXplZCBXaGl0ZSB0ZXN0CnlfQkNUX2xvZ19zdHVkX1cgPC0gbG10ZXN0OjpicHRlc3QoZm9ybXVsYT15X0JDVF9sb2d+dCwgdmFyZm9ybXVsYT15X0JDVF9sb2d+dCtJKHReMiksIHN0dWRlbnRpemU9VFJVRSwgZGF0YT1EYXRhX2RmKQpzaG93KHlfQkNUX2xvZ19zdHVkX1cpCmBgYAoqKlNxdWFyZSBNZXRob2QqKjoKYGBge3J9CiMgVGhlIHN0dWRlbnRpemVkIEJyZXVzY2gtUGFnYW4gdGVzdAp5X0JDVF9zcXJ0X3N0dWRfQlAgPC0gbG10ZXN0OjpicHRlc3QoZm9ybXVsYT15X0JDVF9zcXJ0fnQsIHZhcmZvcm11bGE9TlVMTCwgc3R1ZGVudGl6ZT1UUlVFLCBkYXRhPURhdGFfZGYpCnNob3coeV9CQ1Rfc3FydF9zdHVkX0JQKQojIFdlIGhhdmUgdG8gcmVqZWN0IHRoZSBudWxsIG9mIGhvbW9zY2VkYXN0aWNpdHkgYXQgdGhlIHNpZ25pZmljYW5jZSBsZXZlbCAkXGFscGhhPTAuMSQgb3IgJFxhbHBoYT0xMFwlJC4KCiMgVGhlIHN0dWRlbnRpemVkIFdoaXRlIHRlc3QKeV9CQ1Rfc3FydF9zdHVkX1cgPC0gbG10ZXN0OjpicHRlc3QoZm9ybXVsYT15X0JDVF9zcXJ0fnQsIHZhcmZvcm11bGE9eV9CQ1Rfc3FydH50K0kodF4yKSwgc3R1ZGVudGl6ZT1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YT1EYXRhX2RmKQpzaG93KHlfQkNUX3NxcnRfc3R1ZF9XKQpgYGAKSSByaXN1bHRhdGkgbWlnbGlvcmkgdmVuZ29ubyByZXN0aXR1aXRpIGRhbGxhICoqdHJhc2Zvcm1hdGEgbG9nYXJpdG1pY2EqKiAoQnJldXNjaC1QYWdhbiB0ZXN0IHAtdmFsdWUgPSAwLjg0NjIsIFdoaXRlIHRlc3QgcC12YWx1ZSA9IDAuMTM3MykKCiMjIyMjICoqU1RMIERFQ09NUE9TSVRJT04qKgoKTGEgZGVjb21wb3NpemlvbmUgKlNUTCogw6ggc3RhdGEgYXBwbGljYXRhIGFsbGEgc2VyaWUgdGVtcG9yYWxlIHRyYXNmb3JtYXRhIGFsIGZpbmUgZGkgc3VkZGl2aWRlcmxhIGluIHRyZSBjb21wb25lbnRpOiAqdHJlbmQqLCAqc3RhZ2lvbmFsaXTDoCogZSAqcmVtYWluZGVyKi4gRHVlIHBhcmFtZXRyaSBkZWxsYSBkZWNvbXBvc2l6aW9uZSAqKlNUTCoqIGRldm9ubyBlc3NlcmUgZGVmaW5pdGk6IGxhIGZpbmVzdHJhIHBlciBpbCAqdHJlbmQqIGUgbGEgZmluZXN0cmEgcGVyIGxhICpzdGFnaW9uYWxpdMOgLiogVmVycmFubm8gdGVzdGF0aSBpIHNlZ3VlbnRpIHNldCBkaSBwYXJhbWV0cmkgZSB2ZXJyw6Agc2VsZXppb25hdG8gcXVlbGxvIGNoZSBmb3JuaXNjZSBpbCByZW1haW5kZXIgY29uIGlsIG1pbm9yIHZhbG9yZSBkaSBhdXRvY29ycmVsYXppb25lOgoKLSBGaW5lc3RyYSB0cmVuZCA9IE5VTEwgZSBmaW5lc3RyYSBzdGFnaW9uYWxpdMOgID0gTlVMTCAoaWwgY2hlIHNpZ25pZmljYSBjaGUgbGUgZHVlIGZpbmVzdHJlIHNhcmFubm8gc2VsZXppb25hdGUgYXV0b21hdGljYW1lbnRlIGRhbGxhIGZ1bnppb25lIFIpCi0gRmluZXN0cmEgdHJlbmQgPSAxOSBlIGZpbmVzdHJhIHN0YWdpb25hbGl0w6AgPSAxMQotIEZpbmVzdHJhIHRyZW5kID0gMjMgZSBmaW5lc3RyYSBzdGFnaW9uYWxpdMOgID0gMTEKLSBGaW5lc3RyYSB0cmVuZCA9IDI3IGUgZmluZXN0cmEgc3RhZ2lvbmFsaXTDoCA9IDExCi0gRmluZXN0cmEgdHJlbmQgPSAyOSBlIGZpbmVzdHJhIHN0YWdpb25hbGl0w6AgPSAxMQotIEZpbmVzdHJhIHRyZW5kID0gMzEgZSBmaW5lc3RyYSBzdGFnaW9uYWxpdMOgID0gMTEKClF1ZXN0YSBzY2VsdGEgw6ggc3RhdGEgdmFsdXRhdGEgc3VsbGEgYmFzZSBkZWxsJ2lzcGV6aW9uZSB2aXNpdmEgZGVsbGUgdHJlIGNvbXBvbmVudGkgZGVsbGEgc2VyaWUgdGVtcG9yYWxlLCBkZWxsJ2lzcGV6aW9uZSB2aXNpdmEgZGVnbGkgYXV0b2NvcnJlbGFncmFtbWkgdG90YWxlIGUgcGFyemlhbGUgZSBkZWkgcmlzdWx0YXRpIGRlaSAqdGVzdCogZGkgdmFsdXRhemlvbmUgZGVsbGUgaXBvdGVzaSBkaSBvbW9zY2hlZGFzdGljaXTDoCwgbWFuY2FuemEgZGkgY29ycmVsYXppb25lIGUgc3RhemlvbmFyaWV0w6AgZGVsICpyZW1haW5kZXIgU1RMKi4gTGEgZmluZXN0cmEgZGVsbGEgc3RhZ2lvbmFsaXTDoCDDqCBzdGF0YSBpbXBvc3RhdGEgc3UgMTEgcGVyY2jDqSBhYmJpYW1vIHVuYSBzdGFnaW9uYWxpdMOgIGFubnVhbGUgbmVsbGEgc2VyaWUgdGVtcG9yYWxlLCBtZW50cmUgbGEgZmluZXN0cmEgZGVsIHRyZW5kIHZpZW5lIGNhbWJpYXRhLiBQacO5IGFsdGEgw6ggbGEgZmluZXN0cmEgZGVsIHRyZW5kLCBwacO5IHRlbXBvIChvdnZlcm8gbWVzaSkgw6ggbmVjZXNzYXJpbyBwZXIgaSBjYW1iaWFtZW50aSBkZWwgdHJlbmQuCmBgYHtyfQpDYW1yeV9Ucm5TX3RzIDwtIHRzKENhbXJ5X1RyblNfZGYkeV9CQ1RfbG9nLCBzdGFydD1jKDIwMDksIDAxKSwgZW5kPWMoMjAyMSwgMTEpLCBmcmVxdWVuY3kgPSAxMikKeV9sb2cgPC0gQ2FtcnlfVHJuU19kZiR5X0JDVF9sb2cKQ2FtcnlfVHJuU190c190c2liYmxlIDwtIGFzX3RzaWJibGUoQ2FtcnlfVHJuU190cywga2V5ID0gTlVMTCwgaW5kZXggPSBkYXRlKQpEYXRhX3RzaWJibGUgPC0gQ2FtcnlfVHJuU190c190c2liYmxlCmBgYAoKUGxvdCBkZWxsYSBkZWNvbXBvc2l6aW9uZSBTVEwKYGBge3J9CkRhdGFfdHNpYmJsZSAlPiUKICBtb2RlbChTVEwoeV9sb2cgfiB0cmVuZCh3aW5kb3c9MjkpK3NlYXNvbihwZXJpb2Q9IjEgeWVhciIsIHdpbmRvdz0xMSksIHJvYnVzdD1UUlVFKSkgJT4lCiAgY29tcG9uZW50cygpICU+JSAKICBhdXRvcGxvdCgpK2xhYnMoeD0iRGF0YSIpCmBgYApDcmVhemlvbmUgZGVsbGUgY29tcG9uZW50aSAoKnRyZW5kKiwgKnNlYXNvbmFsaXR5KiwgKnJlbWFpbmRlciopIGRlcml2YXRlIGRhbGxhIGRlY29tcG9zaXppb25lLgpgYGB7cn0KeV9CQ1RfbG9nX1NUTF9kZWZfd2luX2RjbXBfdHMgPC0gRGF0YV90c2liYmxlICU+JQogIG1vZGVsKFNUTCh5X2xvZyB+IHRyZW5kKHdpbmRvdz0yOSkrc2Vhc29uKHBlcmlvZD0iMSB5ZWFyIiwgd2luZG93PTExKSwgcm9idXN0PVRSVUUpKSAlPiUKICBjb21wb25lbnRzKCkKeSA8LSB5X0JDVF9sb2dfU1RMX2RlZl93aW5fZGNtcF90cyRyZW1haW5kZXIKVCA8LSBsZW5ndGgoeSkKbWF4bGFnIDwtIGNlaWxpbmcobWluKDEwLCBULzQpKSAgICAgIyBIeW5kbWFuIChmb3IgZGF0YSB3aXRob3V0IHNlYXNvbmFsaXR5KQpCb3gudGVzdCh5LCBsYWcgPSBtYXhsYWcsdHlwZSA9ICJManVuZy1Cb3giLCBmaXRkZiA9IDApCmBgYAoKYGBge3J9CkRhdGFfZGYgPC0gQ2FtcnlfVHJuU19kZgp5IDwtIHlfQkNUX2xvZ19TVExfZGVmX3dpbl9kY21wX3RzJHJlbWFpbmRlcgpUIDwtIGxlbmd0aCh5KQojIG1heGxhZyA8LSBjZWlsaW5nKDEwKmxvZzEwKFQpKSAgICAjIERlZmF1bHQKIyBtYXhsYWcgPC0gY2VpbGluZyhzcXJ0KG4pKzQ1KSAgICAgIyBCb3gtSmVua2lucwptYXhsYWcgPC0gY2VpbGluZyhtaW4oMTAsIFQvNCkpICAgICAjIEh5bmRtYW4gKGZvciBkYXRhIHdpdGhvdXQgc2Vhc29uYWxpdHkpCiMgbWF4bGFnIDwtIGNlaWxpbmcobWluKDIqMTIsIFQvNSkpICMgSHluZG1hbiAoZm9yIGRhdGEgd2l0aCBzZWFzb25hbGl0eSkKIyBodHRwczovL3JvYmpoeW5kbWFuLmNvbS9oeW5kc2lnaHQvbGp1bmctYm94LXRlc3QvCkF1dF9GdW5feSA8LSBhY2YoeSwgbGFnLm1heD1tYXhsYWcsIHR5cGU9ImNvcnJlbGF0aW9uIiwgcGxvdD1GQUxTRSkKY2lfOTAgPC0gcW5vcm0oKDErMC45MCkvMikvc3FydChUKQpjaV85NSA8LSBxbm9ybSgoMSswLjk1KS8yKS9zcXJ0KFQpCmNpXzk5IDwtIHFub3JtKCgxKzAuOTkpLzIpL3NxcnQoVCkKUGxvdF9BdXRfRnVuX3kgPC0gZGF0YS5mcmFtZShsYWc9QXV0X0Z1bl95JGxhZywgYWNmPUF1dF9GdW5feSRhY2YpCkZpcnN0X0RhdGUgPC0gcGFzdGUoRGF0YV9kZiRNb250aFsxXSxEYXRhX2RmJFllYXJbMV0pCkxhc3RfRGF0ZSA8LSBwYXN0ZShEYXRhX2RmJE1vbnRoW1RdLERhdGFfZGYkWWVhcltUXSkKdGl0bGVfY29udGVudCA8LSBicXVvdGUoYXRvcCgiQXV0b2NvcnJlbG9ncmFtIG9mIHRoZSBSZW1haW5kZXJzIGluIHRoZSBTVEwgRGVjb21wLiBmb3IgdGhlIExvZy4gQm94LUNveCBUcmFuc2YuIG9mIHRoZSBUcmFpbmluZyBTZXQgZnJvbSAiLCAuKEZpcnN0X0RhdGUpLCAiIHRvICIsIC4oTGFzdF9EYXRlKSkpCnN1YnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKHBhc3RlKCJQYXRoIGxlbmd0aCAiLCAuKFQpLCAiIHNhbXBsZSBwb2ludHMuIExhZ3MgIiwgLihtYXhsYWcpKSkKY2FwdGlvbl9jb250ZW50IDwtICJBdXRob3I6IE1hdHRlbyBDaGlhY2NoaWEiCnhfbmFtZSA8LSBicXVvdGUoImxhZ3MiKQp4X2JyZWFrc19udW0gPC0gbWF4bGFnCnhfYmlud2lkdGggPC0gMQp4X2JyZWFrcyA8LSBBdXRfRnVuX3kkbGFnCiNjbGFzcyh4X2JyZWFrcykKeF9sYWJzIDwtIGZvcm1hdCh4X2JyZWFrcywgc2NpZW50aWZpYz1GQUxTRSkKZ2dwbG90KFBsb3RfQXV0X0Z1bl95LCBhZXMoeD1sYWcsIHk9YWNmKSkrCiAgZ2VvbV9zZWdtZW50KGFlcyh4PWxhZywgeT1yZXAoMCxsZW5ndGgobGFnKSksIHhlbmQ9bGFnLCB5ZW5kPWFjZiksIGxpbmV3aWR0aD0xLCBjb2w9ImJsYWNrIikgKwogICMgZ2VvbV9jb2wobWFwcGluZz1OVUxMLCBkYXRhPU5VTEwsIHBvc2l0aW9uPSJkb2RnZSIsIHdpZHRoPTAuMSwgY29sPSJibGFjayIsIGluaGVyaXQuYWVzPVRSVUUpKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9LWNpXzkwLCBjb2xvcj0iQ0lfOTAiKSwgc2hvdy5sZWdlbmQ9VFJVRSwgbHR5PTMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PWNpXzkwLCBjb2xvcj0iQ0lfOTAiKSwgbHR5PTMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PWNpXzk1LCBjb2xvcj0iQ0lfOTUiKSwgc2hvdy5sZWdlbmQ9VFJVRSwgbHR5PTQpKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9LWNpXzk1LCBjb2xvcj0iQ0lfOTUiKSwgbHR5PTQpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PS1jaV85OSwgY29sb3I9IkNJXzk5IiksIHNob3cubGVnZW5kPVRSVUUsIGx0eT00KSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD1jaV85OSwgY29sb3I9IkNJXzk5IiksIGx0eT00KSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9ImxhZyIsIGJyZWFrcz14X2JyZWFrcywgbGFiZWw9eF9sYWJzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWU9ImFjZiB2YWx1ZSIsIGJyZWFrcz13YWl2ZXIoKSwgbGFiZWxzPU5VTEwsCiAgICAgICAgICAgICAgICAgICAgIHNlYy5heGlzPXNlY19heGlzKH4uLCBicmVha3M9d2FpdmVyKCksIGxhYmVscz13YWl2ZXIoKSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iQ29uZi4gSW50ZXIuIiwgbGFiZWxzPWMoIjkwJSIsIjk1JSIsIjk5JSIpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXM9YyhDSV85MD0iZ3JlZW4iLCBDSV85NT0iYmx1ZSIsIENJXzk5PSJyZWQiKSkgKwogIGdndGl0bGUodGl0bGVfY29udGVudCkgKwogIGxhYnMoc3VidGl0bGU9c3VidGl0bGVfY29udGVudCwgY2FwdGlvbj1jYXB0aW9uX2NvbnRlbnQpICsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9OSksIAogICAgICAgIHBsb3Quc3VidGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PSAwLjUsIHNpemU9OC41KSwKICAgICAgICBwbG90LmNhcHRpb249ZWxlbWVudF90ZXh0KGhqdXN0PTEuMCksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aD11bml0KDAuOCwiY20iKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpgYGAKYGBge3J9CkRhdGFfZGYgPC0gQ2FtcnlfVHJuU19kZgp5IDwtIHlfQkNUX2xvZ19TVExfZGVmX3dpbl9kY21wX3RzJHJlbWFpbmRlcgpUIDwtIGxlbmd0aCh5KQojIG1heGxhZyA8LSBjZWlsaW5nKDEwKmxvZzEwKFQpKSAgICAjIERlZmF1bHQKIyBtYXhsYWcgPC0gY2VpbGluZyhzcXJ0KG4pKzQ1KSAgICAgIyBCb3gtSmVua2lucwptYXhsYWcgPC0gY2VpbGluZyhtaW4oMTAsIFQvNCkpICAgICAjIEh5bmRtYW4gKGZvciBkYXRhIHdpdGhvdXQgc2Vhc29uYWxpdHkpCiMgbWF4bGFnIDwtIGNlaWxpbmcobWluKDIqMTIsIFQvNSkpICMgSHluZG1hbiAoZm9yIGRhdGEgd2l0aCBzZWFzb25hbGl0eSkKIyBodHRwczovL3JvYmpoeW5kbWFuLmNvbS9oeW5kc2lnaHQvbGp1bmctYm94LXRlc3QvCkF1dF9GdW5feSA8LSBwYWNmKHksIGxhZy5tYXg9bWF4bGFnLCB0eXBlPSJjb3JyZWxhdGlvbiIsIHBsb3Q9RkFMU0UpCmNpXzkwIDwtIHFub3JtKCgxKzAuOTApLzIpL3NxcnQoVCkKY2lfOTUgPC0gcW5vcm0oKDErMC45NSkvMikvc3FydChUKQpjaV85OSA8LSBxbm9ybSgoMSswLjk5KS8yKS9zcXJ0KFQpClBsb3RfQXV0X0Z1bl95IDwtIGRhdGEuZnJhbWUobGFnPUF1dF9GdW5feSRsYWcsIGFjZj1BdXRfRnVuX3kkYWNmKQpGaXJzdF9EYXRlIDwtIHBhc3RlKERhdGFfZGYkTW9udGhbMV0sRGF0YV9kZiRZZWFyWzFdKQpMYXN0X0RhdGUgPC0gcGFzdGUoRGF0YV9kZiRNb250aFtUXSxEYXRhX2RmJFllYXJbVF0pCnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKGF0b3AoIlBhcnRpYWwgYXV0b2NvcnJlbG9ncmFtIG9mIHRoZSBSZW1haW5kZXJzIGluIHRoZSBTVEwgRGVjb21wLiBmb3IgdGhlIExvZy4gQm94LUNveCBUcmFuc2YuIG9mIHRoZSBUcmFpbmluZyBTZXQgZnJvbSAiLCAuKEZpcnN0X0RhdGUpLCAiIHRvICIsIC4oTGFzdF9EYXRlKSkpCnN1YnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKHBhc3RlKCJQYXRoIGxlbmd0aCAiLCAuKFQpLCAiIHNhbXBsZSBwb2ludHMuIExhZ3MgIiwgLihtYXhsYWcpKSkKY2FwdGlvbl9jb250ZW50IDwtICJBdXRob3I6IE1hdHRlbyBDaGlhY2NoaWEiCnhfbmFtZSA8LSBicXVvdGUoImxhZ3MiKQp4X2JyZWFrc19udW0gPC0gbWF4bGFnCnhfYmlud2lkdGggPC0gMQp4X2JyZWFrcyA8LSBBdXRfRnVuX3kkbGFnCiNjbGFzcyh4X2JyZWFrcykKeF9sYWJzIDwtIGZvcm1hdCh4X2JyZWFrcywgc2NpZW50aWZpYz1GQUxTRSkKZ2dwbG90KFBsb3RfQXV0X0Z1bl95LCBhZXMoeD1sYWcsIHk9YWNmKSkrCiAgZ2VvbV9zZWdtZW50KGFlcyh4PWxhZywgeT1yZXAoMCxsZW5ndGgobGFnKSksIHhlbmQ9bGFnLCB5ZW5kPWFjZiksIGxpbmV3aWR0aD0xLCBjb2w9ImJsYWNrIikgKwogICMgZ2VvbV9jb2wobWFwcGluZz1OVUxMLCBkYXRhPU5VTEwsIHBvc2l0aW9uPSJkb2RnZSIsIHdpZHRoPTAuMSwgY29sPSJibGFjayIsIGluaGVyaXQuYWVzPVRSVUUpKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9LWNpXzkwLCBjb2xvcj0iQ0lfOTAiKSwgc2hvdy5sZWdlbmQ9VFJVRSwgbHR5PTMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PWNpXzkwLCBjb2xvcj0iQ0lfOTAiKSwgbHR5PTMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PWNpXzk1LCBjb2xvcj0iQ0lfOTUiKSwgc2hvdy5sZWdlbmQ9VFJVRSwgbHR5PTQpKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9LWNpXzk1LCBjb2xvcj0iQ0lfOTUiKSwgbHR5PTQpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PS1jaV85OSwgY29sb3I9IkNJXzk5IiksIHNob3cubGVnZW5kPVRSVUUsIGx0eT00KSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD1jaV85OSwgY29sb3I9IkNJXzk5IiksIGx0eT00KSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9ImxhZyIsIGJyZWFrcz14X2JyZWFrcywgbGFiZWw9eF9sYWJzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWU9ImFjZiB2YWx1ZSIsIGJyZWFrcz13YWl2ZXIoKSwgbGFiZWxzPU5VTEwsCiAgICAgICAgICAgICAgICAgICAgIHNlYy5heGlzPXNlY19heGlzKH4uLCBicmVha3M9d2FpdmVyKCksIGxhYmVscz13YWl2ZXIoKSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iQ29uZi4gSW50ZXIuIiwgbGFiZWxzPWMoIjkwJSIsIjk1JSIsIjk5JSIpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXM9YyhDSV85MD0iZ3JlZW4iLCBDSV85NT0iYmx1ZSIsIENJXzk5PSJyZWQiKSkgKwogIGdndGl0bGUodGl0bGVfY29udGVudCkgKwogIGxhYnMoc3VidGl0bGU9c3VidGl0bGVfY29udGVudCwgY2FwdGlvbj1jYXB0aW9uX2NvbnRlbnQpICsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9OSksIAogICAgICAgIHBsb3Quc3VidGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PSAwLjUsIHNpemU9OC41KSwKICAgICAgICBwbG90LmNhcHRpb249ZWxlbWVudF90ZXh0KGhqdXN0PTEuMCksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aD11bml0KDAuOCwiY20iKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpgYGAKVHV0dGkgaSBzZXQgZGkgcGFyYW1ldHJpIFNUTCB0ZXN0YXRpIGZvcm5pc2Nvbm8gdW4gKnJlbWFpbmRlciogY2hlIHB1w7IgZXNzZXJlIGNvbnNpZGVyYXRvIG9tb3NjaGVkYXN0aWNvIGUgc3RhemlvbmFyaW8uIFBlciBxdWFudG8gcmlndWFyZGEgbGEgY29ycmVsYXppb25lLCBpbnZlY2UsIHR1dHRpIGkgc2V0IGZvcm5pc2Nvbm8gdW4gKnJlbWFpbmRlciogYXV0b2NvcnJlbGF0bywgZGkgY29uc2VndWVuemEgc2kgYW5kcsOgIGEgdXRsaXp6YXJlIGlsIG1vZGVsbG8gKipBUk1BKiogcGVyIHBvdGVyIGRlc2NyaXZlcmUgbmVsIG1vZG8gbWlnbGlvcmUgcXVlc3RvIHRpcG8gZGkgYXV0b2NvcnJlbGF6aW9uZS4gCgpMJ2FuYWxpc2kgdmVycsOgIGNvbnRpbnVhdGEgdXRpbGl6emFuZG8gbGEgZGVjb21wb3NpemlvbmUgZGkgdGVtcG86CgotICp0cmVuZCB3aW5kb3cqID0gMjkKLSAqc2Vhc29uIHdpbmRvdyogPSAxMQoKTCdpZGVhLCBpbmZhdHRpLCDDqCBzdGF0YSBxdWVsbGEgZGkgY2F0dHVyYXJlIGFsIG1lZ2xpbyBsZSB2YXJpYXppb25lIGEgbHVuZ28gdGVybWluZSBwaXV0dG9zdG8gY2hlIHF1ZWxsZSBhIGJyZXZlIHRlcm1pbmUuCmBgYHtyfQp5X0JDVF9sb2dfU1RMX3JlbWFpbmRlciA8LSB5X0JDVF9sb2dfU1RMX2RlZl93aW5fZGNtcF90cyRyZW1haW5kZXIKYGBgCgojIyMjIyAqKkFSTUEgbW9kZWwqKgoKSSBtb2RlbGxpICoqQVJNQSoqICgqQXV0b3JlZ3Jlc3NpdmUgTW92aW5nIEF2ZXJhZ2UqKSBzb25vIHVuYSBjbGFzc2UgZGkgbW9kZWxsaSBzdGF0aXN0aWNpIHV0aWxpenphdGkgcGVyIGFuYWxpenphcmUgc2VyaWUgdGVtcG9yYWxpLiBRdWVzdGkgbW9kZWxsaSBjb21iaW5hbm8gZHVlIGNvbXBvbmVudGkgcHJpbmNpcGFsaTogbCcqYXV0b3JlZ3Jlc3Npb25lKiAoKipBUioqKSBlIGxhICptZWRpYSBtb2JpbGUqICgqKk1BKiopLgoKSWwgY29tcG9uZW50ZSAqKkFSKiogdXRpbGl6emEgaSB2YWxvcmkgcHJlY2VkZW50aSBkZWxsYSBzZXJpZSB0ZW1wb3JhbGUgcGVyIHByZXZlZGVyZSBpIHZhbG9yaSBmdXR1cmkuIENpw7Igc2lnbmlmaWNhIGNoZSBpbCB2YWxvcmUgY29ycmVudGUgZGlwZW5kZSBkYWxsZSBvc3NlcnZhemlvbmkgcHJlY2VkZW50aS4gSWwgZ3JhZG8gZGkgZGlwZW5kZW56YSBkYWkgdmFsb3JpIHByZWNlZGVudGkgZGlwZW5kZSBkYWxsJ29yZGluZSBkZWwgbW9kZWxsbyBBUi4KCklsIGNvbXBvbmVudGUgKipNQSoqLCBkJ2FsdHJhIHBhcnRlLCB1dGlsaXp6YSBsJ2Vycm9yZSByZXNpZHVvIGRlbGxhIHByZXZpc2lvbmUgZGVsbCdhdXRvcmVncmVzc2lvbmUgcGVyIHByZXZlZGVyZSBpIHZhbG9yaSBmdXR1cmkuIEwnb3JkaW5lIGRlbCBtb2RlbGxvIE1BIGRldGVybWluYSBpbCBudW1lcm8gZGkgZXJyb3JpIHJlc2lkdWkgdXRpbGl6emF0aSBuZWxsYSBwcmV2aXNpb25lLgoKTCdpZGVhIMOoIHF1ZWxsYSBkaSBwb3RlciByYXBwcmVzZW50YXJlIGlsICpyZW1haW5kZXIqIHRyYW1pdGUgdW4gbW9kZWxsbyAqKkFSTUEqKi4KU29ubyBzdGF0aSBhZG90dGF0aSBkdWUgZGl2ZXJzaSBhcHByb2NjaSBpbiAqUiogcGVyIGlkZW50aWZpY2FyZSBpbCBtaWdsaW9yIG1vZGVsbG8gKkFSTUEuKiBVbm8gc2kgYmFzYSBzdSB1bmEgZnVuemlvbmUgaW50ZWdyYXRhIGRpICpSKiAoIiphdXRvLmFyaW0qYSIpLCBtZW50cmUgbCdhbHRybyBzaSBiYXNhIHN1bGxhIHNlbGV6aW9uZSBtYW51YWxlIGRlbCBtaWdsaW9yIG1vZGVsbG8gKkFSTUEqIHRyYSBxdWVsbGkgdGVzdGF0aS4gRW50cmFtYmkgaSBtZXRvZGkgc2VsZXppb25hbm8gaWwgbWlnbGlvciBtb2RlbGxvIHRyYW1pdGUgaWwgdmFsb3JlIHBpw7kgYmFzc28gZGkgKkFJQyogb3R0ZW51dG8uCkluIHN0YXRpc3RpY2EsIGwnKkFJQyogKCpBa2Fpa2UncyBJbmZvcm1hdGlvbiBDcml0ZXJpb24qKSDDqCB1bmEgbWlzdXJhIGRpIGJvbnTDoCBkaSBhZGF0dGFtZW50byBkaSB1biBtb2RlbGxvIHN0YXRpc3RpY28gYWkgZGF0aSBvc3NlcnZhdGkuIEVzc28gZm9ybmlzY2UgdW5hIHN0aW1hIHJlbGF0aXZhIGRlbGxhIHF1YWxpdMOgIGRpIHVuIG1vZGVsbG8gcmlzcGV0dG8gYWQgYWx0cmkgbW9kZWxsaSBhbHRlcm5hdGl2aS4KCkwnaWRlYSBhbGxhIGJhc2UgZGVsbCcqQUlDKiDDqCBkaSBwZW5hbGl6emFyZSBpIG1vZGVsbGkgY2hlIGluY2x1ZG9ubyB0cm9wcGkgcGFyYW1ldHJpIHJpc3BldHRvIGFsIG51bWVybyBkaSBvc3NlcnZhemlvbmksIHBvaWNow6kgcXVlc3RpIG1vZGVsbGkgcmlzY2hpYW5vIGRpIHNvdnJhLWFkYXR0YXJzaSBhaSBkYXRpIGRpIGFwcHJlbmRpbWVudG8gZSBkaSBwZXJkZXJlIGxhIGNhcGFjaXTDoCBkaSBnZW5lcmFsaXp6YXJlIGFkIGFsdHJpIGRhdGkgKCpvdmVyZml0dGluZyopLiBQZXJ0YW50bywgdHJhIGkgbW9kZWxsaSBjaGUgZm9ybmlzY29ubyB1biBidW9uIGFkYXR0YW1lbnRvIGFpIGRhdGksIGwnKkFJQyogc3VnZ2VyaXNjZSBkaSBzY2VnbGllcmUgcXVlbGxvIGNvbiBpbCB2YWxvcmUgbWlub3JlLgpgYGB7cn0KeSA8LSB5X0JDVF9sb2dfU1RMX3JlbWFpbmRlcgpBUklNQV95IDwtIGxpc3QoKQojIFNldHRpbmcgYSBjb3VudGVyCmNuIDwtIDEKIyBMb29waW5nIG92ZXIgaW5jbHVkZS5tZWFuCmZvcihmbGFnIGluIDA6MSl7CiAgIyBMb29waW5nIG92ZXIgcAogIGZvcihwIGluIDA6Nil7CiAgICAjIExvb3Bpbmcgb3ZlciBxCiAgICBmb3IocSBpbiAwOjYpewogICAgI0VSUk9SIGFuZCBXQVJOSU5HUyBIQU5ETElORwogICAgdHJ5Q2F0Y2goewogICAgICBBUklNQV95W1tjbl1dIDwtIGFyaW1hKHksIG9yZGVyPWMocCwwLHEpLCBpbmNsdWRlLm1lYW49ZmxhZywgbWV0aG9kPSJDU1MtTUwiKQogICAgICBjbiA8LSBjbisxCiAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpewogICAgfSwgd2FybmluZyA9IGZ1bmN0aW9uICh3KXsKICAgIH0KICAgICkKICB9CiB9IAp9CgojIFNlbGVjdGluZyB0aGUgYmVzdCBtb2RlbCBieSB0aGUgQWthaWtlIEluZm9ybWF0aW9uIENyaXRlcml1bQpBUklNQV95X0FJQyA8LSBzYXBwbHkoQVJJTUFfeSwgZnVuY3Rpb24oeCkgeCRhaWMpCiNzaG93KEFSSU1BX3lfQUlDKQpBUklNQV95X21pbl9BSUMgPC0gQVJJTUFfeVtbd2hpY2goQVJJTUFfeV9BSUM9PW1pbihBUklNQV95X0FJQykpXV0Kc2hvdyhBUklNQV95X21pbl9BSUMpCmBgYApUcmFtaXRlIGxhIHNlbGV6aW9uZSBtYW51YWxlIG90dGVuaWFtbyBjaGUgaWwgbW9kZWxsbyAqKkFSSU1BICg0LDAsNikqKiDDqCBxdWVsbG8gY2hlIHJlc3RpdHVpc2NlIHVuIHZhbG9yZSBkaSBBSUMgbWlub3JlLgoKQWRlc3NvIHNpIHN0dWRpYSBsYSBjb3JyZWxhemlvbmUgZGVpIHJlc2lkdWkgZGVsIG1vZGVsbG8gdHJhbWl0ZSBpbCB0ZXN0IGRpIExCIHRlc3QuCmBgYHtyfQpUIDwtIGxlbmd0aCh5KQptYXhsYWcgPC0gY2VpbGluZyhtaW4oMTAsIFQvNCkpIApBUklNQV95X21pbl9BSUNfTEIgPC0gQm94LnRlc3QoQVJJTUFfeVtbd2hpY2goQVJJTUFfeV9BSUM9PW1pbihBUklNQV95X0FJQykpXV1bWyJyZXNpZHVhbHMiXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhZz1tYXhsYWcpCnNob3coQVJJTUFfeV9taW5fQUlDX0xCKQpgYGAKSWwgKnAtdmFsdWUqIHJlc2l0dWl0byBjaSBzdWdnZXJpc2NlIGNoZSBub24gw6ggcHJlc2VudGUgY29ycmVsYXppb25lIG5laSByZXNpZHVpIGRlbCBtb2RlbGxvICpBUklNQSAoNCwwLDYpKi4KClZpZW5lIHV0aWxpenphdGEgYW5jaGUgbGEgZnVuemlvbmUgYXV0b21hdGljYSBwZXIgdmVyaWZpY2FyZSBxdWFsZSDDqCBpbCBtaWdsaW9yIG1vZGVsbG8gYWRhdHRvIGEgZGVzY3JpdmVyZSBpbCAqcmVtYWluZGVyIFNUTCouCmBgYHtyfQp5IDwtIHlfQkNUX2xvZ19TVExfcmVtYWluZGVyCkFVVE9BUklNQV95X0FJQyA8LSBhdXRvLmFyaW1hKHksIHN0YXJ0LnA9MCwgbWF4LnA9Niwgc3RhcnQucT0wLCBtYXgucT02LCBtYXgub3JkZXI9MTIsIGljPSJhaWMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsb3dtZWFuPVRSVUUsIHRyYWNlPVRSVUUsIHN0ZXB3aXNlPUZBTFNFLCBubW9kZWxzPTk0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXBwcm94aW1hdGlvbj1GQUxTRSkKYGBgCkxhIGZ1bnppb25lIGF1dG9tYXRpY2EgcmVzaXR1aXNjZSBjb21lIG1pZ2xpb3IgbW9kZWxsbyBpbCBtb2RlbGxvICpBUklNQSAoNiwwLDEpKi4KRGFpIHZhbG9yaSByZXN0aXR1aXRpLCBwZXLDsiwgc2kgbm90YSBjb21lIGFuY2hlIGlsIG1vZGVsbG8gKkFSSU1BICgxLDAsMCkqIHJlc3RpdHVpc2NhIHVuIHZhbG9yZSBkaSBBSUMgbW9sdG8gc2ltaWxlIGFsIHByZWNlZGVudGUuIApQZXIgZGVjaWRpZGVyZSBxdWFsZSBtb2RlbGxvIHV0aWxpenphcmUgc2kgZWZmZXR0dWFubyBpIHZhcmkgdGVzdCBlIHNpIHZlcmlmaWNhLCBwcmVuZGVuZG8gaW4gY29uc2lkZXJhemlvbmUgYW5jaGUgbGEgY29tcGxlc3NpdMOgIGRlbCBtb2RlbGxvLCBxdWFsZSByZXN0aXR1aXNjZSBpIHZhbG9yaSBtaWdsaW9yaS4KCioqQVJJTUEgKDQsMCw2KSoqCmBgYHtyfQp5IDwteV9CQ1RfbG9nX1NUTF9yZW1haW5kZXIKbnVtX2xhZ3MgPC0gMCAgICMgU2V0dGluZyB0aGUgbGFnIHBhcmFtZXRlciBmb3IgdGhlIHRlc3QuCkFVVE9BUklNQV95X0FJQyA8LSBhcmltYSh5LCBvcmRlcj1jKDQsMCw2KSwgaW5jbHVkZS5tZWFuPUZBTFNFLCBtZXRob2Q9IkNTUy1NTCIpCmFyaW1hX3Jlcz1BVVRPQVJJTUFfeV9BSUNbWydyZXNpZHVhbHMnXV0KIyBUaGUgc3R1ZGVudGl6ZWQgQlAgdGVzdApBUklNQV95X0FJQ19CUCA8LSBsbXRlc3Q6OmJwdGVzdChmb3JtdWxhPWFyaW1hX3Jlc350LCB2YXJmb3JtdWxhPU5VTEwsIHN0dWRlbnRpemU9VFJVRSwgZGF0YT1EYXRhX2RmKQpzaG93KEFSSU1BX3lfQUlDX0JQKQojIFRoZSBzdHVkZW50aXplZCBXaGl0ZSB0ZXN0CkFSSU1BX3lfQUlDX1cgPC0gbG10ZXN0OjpicHRlc3QoZm9ybXVsYT1hcmltYV9yZXN+dCwgdmFyZm9ybXVsYT1hcmltYV9yZXN+dCtJKHReMiksIHN0dWRlbnRpemU9VFJVRSwgZGF0YT1EYXRhX2RmKQpzaG93KEFSSU1BX3lfQUlDX1cpCiMgVGhlIEFERiB0ZXN0CkFSSU1BX3lfQUlDX0FERiA8LSB1ci5kZihBVVRPQVJJTUFfeV9BSUNbWydyZXNpZHVhbHMnXV0sIHR5cGU9Im5vbmUiLCBsYWdzPW51bV9sYWdzLCBzZWxlY3RsYWdzPSJGaXhlZCIpICAgIApzdW1tYXJ5KEFSSU1BX3lfQUlDX0FERikgIAojIFRoZSBLUFNTIHRlc3QKQVJJTUFfeV9BSUNfS1BTUyA8LSB1ci5rcHNzKEFVVE9BUklNQV95X0FJQ1tbJ3Jlc2lkdWFscyddXSwgdHlwZT0ibXUiLCBsYWdzPSJuaWwiLCB1c2UubGFnPU5VTEwpICAgIApzdW1tYXJ5KEFSSU1BX3lfQUlDX0tQU1MpICAgICMgU2hvd2luZyB0aGUgcmVzdWx0IG9mIHRoZSB0ZXN0CiMgVGhlIExCIHRlc3QKQVJJTUFfeV9BSUNfTEIgPC0gQm94LnRlc3QoQVVUT0FSSU1BX3lfQUlDW1sicmVzaWR1YWxzIl1dLCBsYWc9bWF4bGFnKQpzaG93KEFSSU1BX3lfQUlDX0xCKQpgYGAKKipBUklNQSAoNiwwLDEpKioKYGBge3J9CkFVVE9BUklNQV95X0FJQyA8LSBhcmltYSh5LCBvcmRlcj1jKDYsMCwxKSwgaW5jbHVkZS5tZWFuPUZBTFNFLCBtZXRob2Q9IkNTUy1NTCIpCmFyaW1hX3Jlcz1BVVRPQVJJTUFfeV9BSUNbWydyZXNpZHVhbHMnXV0KIyBUaGUgc3R1ZGVudGl6ZWQgQlAgdGVzdApBUklNQV95X0FJQ19CUCA8LSBsbXRlc3Q6OmJwdGVzdChmb3JtdWxhPWFyaW1hX3Jlc350LCB2YXJmb3JtdWxhPU5VTEwsIHN0dWRlbnRpemU9VFJVRSwgZGF0YT1EYXRhX2RmKQpzaG93KEFSSU1BX3lfQUlDX0JQKQojIFRoZSBzdHVkZW50aXplZCBXaGl0ZSB0ZXN0CkFSSU1BX3lfQUlDX1cgPC0gbG10ZXN0OjpicHRlc3QoZm9ybXVsYT1hcmltYV9yZXN+dCwgdmFyZm9ybXVsYT1hcmltYV9yZXN+dCtJKHReMiksIHN0dWRlbnRpemU9VFJVRSwgZGF0YT1EYXRhX2RmKQpzaG93KEFSSU1BX3lfQUlDX1cpCiMgVGhlIEFERiB0ZXN0CkFSSU1BX3lfQUlDX0FERiA8LSB1ci5kZihBVVRPQVJJTUFfeV9BSUNbWydyZXNpZHVhbHMnXV0sIHR5cGU9Im5vbmUiLCBsYWdzPW51bV9sYWdzLCBzZWxlY3RsYWdzPSJGaXhlZCIpICAgIApzdW1tYXJ5KEFSSU1BX3lfQUlDX0FERikgIAojIFRoZSBLUFNTIHRlc3QKQVJJTUFfeV9BSUNfS1BTUyA8LSB1ci5rcHNzKEFVVE9BUklNQV95X0FJQ1tbJ3Jlc2lkdWFscyddXSwgdHlwZT0ibXUiLCBsYWdzPSJuaWwiLCB1c2UubGFnPU5VTEwpICAgIApzdW1tYXJ5KEFSSU1BX3lfQUlDX0tQU1MpICAgICMgU2hvd2luZyB0aGUgcmVzdWx0IG9mIHRoZSB0ZXN0CiMgVGhlIExCIHRlc3QKQVJJTUFfeV9BSUNfTEIgPC0gQm94LnRlc3QoQVVUT0FSSU1BX3lfQUlDW1sicmVzaWR1YWxzIl1dLCBsYWc9bWF4bGFnKQpzaG93KEFSSU1BX3lfQUlDX0xCKQpgYGAKKipBUklNQSAoMSwwLDApKioKYGBge3J9CkFVVE9BUklNQV95X0FJQyA8LSBhcmltYSh5LCBvcmRlcj1jKDEsMCwwKSwgaW5jbHVkZS5tZWFuPUZBTFNFLCBtZXRob2Q9IkNTUy1NTCIpCmFyaW1hX3Jlcz1BVVRPQVJJTUFfeV9BSUNbWydyZXNpZHVhbHMnXV0KCiMgVGhlIHN0dWRlbnRpemVkIEJQIHRlc3QKQVJJTUFfeV9BSUNfQlAgPC0gbG10ZXN0OjpicHRlc3QoZm9ybXVsYT1hcmltYV9yZXN+dCwgdmFyZm9ybXVsYT1OVUxMLCBzdHVkZW50aXplPVRSVUUsIGRhdGE9RGF0YV9kZikKc2hvdyhBUklNQV95X0FJQ19CUCkKCiMgVGhlIHN0dWRlbnRpemVkIFdoaXRlIHRlc3QKQVJJTUFfeV9BSUNfVyA8LSBsbXRlc3Q6OmJwdGVzdChmb3JtdWxhPWFyaW1hX3Jlc350LCB2YXJmb3JtdWxhPWFyaW1hX3Jlc350K0kodF4yKSwgc3R1ZGVudGl6ZT1UUlVFLCBkYXRhPURhdGFfZGYpCnNob3coQVJJTUFfeV9BSUNfVykKCiMgVGhlIEFERiB0ZXN0CkFSSU1BX3lfQUlDX0FERiA8LSB1ci5kZihBVVRPQVJJTUFfeV9BSUNbWydyZXNpZHVhbHMnXV0sIHR5cGU9Im5vbmUiLCBsYWdzPW51bV9sYWdzLCBzZWxlY3RsYWdzPSJGaXhlZCIpICAgIApzdW1tYXJ5KEFSSU1BX3lfQUlDX0FERikgIAoKIyBUaGUgS1BTUyB0ZXN0CkFSSU1BX3lfQUlDX0tQU1MgPC0gdXIua3BzcyhBVVRPQVJJTUFfeV9BSUNbWydyZXNpZHVhbHMnXV0sIHR5cGU9Im11IiwgbGFncz0ibmlsIiwgdXNlLmxhZz1OVUxMKSAgICAKc3VtbWFyeShBUklNQV95X0FJQ19LUFNTKSAgICAjIFNob3dpbmcgdGhlIHJlc3VsdCBvZiB0aGUgdGVzdAoKIyBUaGUgTEIgdGVzdApBUklNQV95X0FJQ19MQiA8LSBCb3gudGVzdChBVVRPQVJJTUFfeV9BSUNbWyJyZXNpZHVhbHMiXV0sIGxhZz1tYXhsYWcpCnNob3coQVJJTUFfeV9BSUNfTEIpCmBgYApSaWFzc3VtZW50byBhYmJpYW1vIGkgc2VndWVudGkgcC12YWx1ZXM6CgotICoqQVJJTUEgKDQsMCw2KSoqCiAgLSAqT21vc2NoZWRhc3RpY2l0w6AqCiAgICAtIEJyZXVzY2gtUGFnYW4gdGVzdDogMC45ODAxCiAgICAtIFdoaXRlIHRlc3Q6IDAuMjM1OQogIC0gKlN0YXppb25hcmlldMOgKgogICAgLSBEaWNrZXktRnVsbGVyIHRlc3Q6IDwgMC4wMQogICAgLSBLcHNzIHRlc3Q6ID4gMC4xCiAgLSAqTm9uIGNvcnJlbGF6aW9uZSoKICAgIC0gTGp1bmctQm94IHRlc3Q6IDAuOTcyNgogICAgCi0gKipBUklNQSAoNiwwLDEpKioKICAtICpPbW9zY2hlZGFzdGljaXTDoCoKICAgIC0gQnJldXNjaC1QYWdhbiB0ZXN0OiAwLjc3NDEKICAgIC0gV2hpdGUgdGVzdDogMC4yMDUzCiAgLSAqU3RhemlvbmFyaWV0w6AqCiAgICAtIERpY2tleS1GdWxsZXIgdGVzdDogPCAwLjAxCiAgICAtIEtwc3MgdGVzdDogPiAwLjEKICAtICpOb24gY29ycmVsYXppb25lKgogICAgLSBManVuZy1Cb3ggdGVzdDogMC45OTU5CgotICoqQVJJTUEgKDEsMCwwKSoqCiAgLSAqT21vc2NoZWRhc3RpY2l0w6AqCiAgICAtIEJyZXVzY2gtUGFnYW4gdGVzdDogMC45MzE3CiAgICAtIFdoaXRlIHRlc3Q6IDAuMTkxOAogIC0gKlN0YXppb25hcmlldMOgKgogICAgLSBEaWNrZXktRnVsbGVyIHRlc3Q6IDwgMC4wMQogICAgLSBLcHNzIHRlc3Q6ID4gMC4xCiAgLSAqTm9uIGNvcnJlbGF6aW9uZSoKICAgIC0gTGp1bmctQm94IHRlc3Q6IDAuMTc0MgoKSW4gdHV0dGkgaSBjYXNpIHNpIG90dGllbmUgdW4gcC12YWx1ZSBlbGV2YXRvIHBlciBpbCAqQlAgdGVzdCogaW5kaWNhbmRvIHF1aW5kaSBsJ29tb3NjaGVkYXN0aWNpdMOgIGRlaSByZXNpZHVpIGRlaSB0cmUgbW9kZWxsaS4gTCdlbGV2YXRvIHAtdmFsdWUgZGVsICpMQiB0ZXN0KiBub24gY2kgcGVybWV0dGUgZGkgcmlnZXR0YXJlIGwnaXBvdGVzaSBudWxsYSBkaSByZXNpZHVpIGdlbmVyYXRpIGRhIHJ1bW9yZSBiaWFuY28uIEluZmluZSwgaWwgcC12YWx1ZSBiYXNzbyBkZWwgKkFERiB0ZXN0KiBzdGEgYSBpbmRpY2FyZSBjaGUgaSByZXNpZHVpIHBvc3Nvbm8gZXNzZXJlIGNvbnNpZGVyYXRpIHN0YXppb25hcmkgY29uIHVuIGxpdmVsbG8gZGkgY29uZmlkZW56YSBkZWxsJzElLCBtZW50cmUgaWwgKktQU1MgdGVzdCosIGNoZSByZXN0aXVpc2NlIHVuIHZhbG9yZSBkZWwgcC12YWx1ZSBtYWdnaW9yZSBkaSAwLjA1LCBpbmRpY2EgY2hlIGwnaXBvdGVzaSBudWxsYSBkaSBzdGF6aW9uYXJpZXTDoCBub24gw6ggcmlnZXR0YWJpbGUuCgpTaSBjb250aW51ZXLDoCBsJ2FuYWxpc2kgdXRpbGl6emFuZG8gaWwgbW9kZWxsbyAqKkFSSU1BICg0LDAsNikqKiBlc3NlbmRvIHF1ZWxsbyBjaGUgcmVzaXR1aXNjZSB2YWxvcmkgbWlnbGlvcmkgZGVpIHJlc2lkdWkgaW4gdGVybWluaSBkaSAqT21vc2NoZWRhc3RpY2l0w6AqICpTdGF6aW9uYXJpZXTDoCogZSAqbm9uIGNvcnJlbGF6aW9uZSosIGxhc2NpYW5kbyBzdXBwb3JyZSBjaGUgw6ggcXVpbmRpIGlsIG1vZGVsbG8gaW4gZ3JhZG8gZGkgZGVzY3JpdmVyZSBpbiBtYW5pZXJhIG1pZ2xpb3JlIGlsICpyZW1haW5kZXIgU1RMKi4KRXF1YXppb25lIGRlbCBtb2RlbGxvOiAkWF90ID3Opl8xWF97dOKIkjF9K86mXzJYX3t04oiSMn0rLi4uK86mXzRYX3t04oiSNH3iiJLOmF8xV197dOKIkjF94oiSzphfMldfe3TiiJIyfeKIki4uLuKIks6YXzZXX3t04oiSNn0rIFdfIHQkCmBgYHtyfQp5IDwtIHlfQkNUX2xvZ19TVExfZGVmX3dpbl9kY21wX3RzJHJlbWFpbmRlcgpBVVRPQVJJTUFfeV9BSUMgPC0gYXJpbWEoeSwgb3JkZXI9Yyg0LDAsNiksIGluY2x1ZGUubWVhbj1GQUxTRSwgbWV0aG9kPSJDU1MtTUwiKQphdXRvcGxvdCh0cyhmaXR0ZWQoQVVUT0FSSU1BX3lfQUlDKSAsIHN0YXJ0PWMoMjAwOSwgMDEpLCBlbmQ9YygyMDIxLCAxMSksIGZyZXF1ZW5jeSA9IDEyKSwgc2VyaWVzPSAiYXJpbWEiKSArCiAgYXV0b2xheWVyKHRzKHlfQkNUX2xvZ19TVExfcmVtYWluZGVyICwgc3RhcnQ9YygyMDA5LCAwMSksIGVuZD1jKDIwMjEsIDExKSwgZnJlcXVlbmN5ID0gMTIpLCBzZXJpZXM9InJlbWFpbmRlciIpICsKICB4bGFiKCJNb250aCIpICsgeWxhYigicmVtIikgKwogIGdndGl0bGUoIlJlbWFpbmRlciBUb3lvdGEgQ2FtcnkiKSArCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IkxlZ2VuZCIpKQpgYGAKUGVyIGNvbXBsZXRlenphIHZlZGlhbW8gYW5jaGUgZ2xpIGF1dG9jb3JyZWxvcmFtbWkuCmBgYHtyfQpEYXRhX2RmIDwtIENhbXJ5X1RyblNfZGYKeSA8LSByZXNpZChBVVRPQVJJTUFfeV9BSUMpCiNjbGFzcyh5KQpUIDwtIGxlbmd0aCh5KQojIG1heGxhZyA8LSBjZWlsaW5nKDEwKmxvZzEwKFQpKSAgICAjIERlZmF1bHQKIyBtYXhsYWcgPC0gY2VpbGluZyhzcXJ0KG4pKzQ1KSAgICAgIyBCb3gtSmVua2lucwptYXhsYWcgPC0gY2VpbGluZyhtaW4oMTAsIFQvNCkpICAgICAjIEh5bmRtYW4gKGZvciBkYXRhIHdpdGhvdXQgc2Vhc29uYWxpdHkpCiMgbWF4bGFnIDwtIGNlaWxpbmcobWluKDIqMTIsIFQvNSkpICMgSHluZG1hbiAoZm9yIGRhdGEgd2l0aCBzZWFzb25hbGl0eSkKIyBodHRwczovL3JvYmpoeW5kbWFuLmNvbS9oeW5kc2lnaHQvbGp1bmctYm94LXRlc3QvCkF1dF9GdW5feSA8LSBhY2YoeSwgbGFnLm1heD1tYXhsYWcsIHR5cGU9ImNvcnJlbGF0aW9uIiwgcGxvdD1GQUxTRSkKY2lfOTAgPC0gcW5vcm0oKDErMC45MCkvMikvc3FydChUKQpjaV85NSA8LSBxbm9ybSgoMSswLjk1KS8yKS9zcXJ0KFQpCmNpXzk5IDwtIHFub3JtKCgxKzAuOTkpLzIpL3NxcnQoVCkKUGxvdF9BdXRfRnVuX3kgPC0gZGF0YS5mcmFtZShsYWc9QXV0X0Z1bl95JGxhZywgYWNmPUF1dF9GdW5feSRhY2YpCkZpcnN0X0RhdGUgPC0gcGFzdGUoRGF0YV9kZiRNb250aFsxXSxEYXRhX2RmJFllYXJbMV0pCkxhc3RfRGF0ZSA8LSBwYXN0ZShEYXRhX2RmJE1vbnRoW1RdLERhdGFfZGYkWWVhcltUXSkKdGl0bGVfY29udGVudCA8LSBicXVvdGUoYXRvcCgiQXV0b2NvcnJlbG9ncmFtIG9mIHRoZSBSZXNpZHVhbHMgb2YgdGhlIEFSSU1BIG1vZGVsIGZvciB0aGUgUmVtYWluZGVycyBpbiB0aGUgU1RMIERlY29tcC4gZm9yIHRoZSBMb2cgQm94LUNveCBUcmFuc2YuIG9mIHRoZSBUcmFpbmluZyBTZXQgZnJvbSAiLCAuKEZpcnN0X0RhdGUpLCAiIHRvICIsIC4oTGFzdF9EYXRlKSkpCnN1YnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKHBhc3RlKCJQYXRoIGxlbmd0aCAiLCAuKFQpLCAiIHNhbXBsZSBwb2ludHMuIExhZ3MgIiwgLihtYXhsYWcpKSkKY2FwdGlvbl9jb250ZW50IDwtICJBdXRob3I6IE1hdHRlbyBDaGlhY2NoaWEiCnhfbmFtZSA8LSBicXVvdGUoImxhZ3MiKQp4X2JyZWFrc19udW0gPC0gbWF4bGFnCnhfYmlud2lkdGggPC0gMQp4X2JyZWFrcyA8LSBBdXRfRnVuX3kkbGFnCnhfbGFicyA8LSBmb3JtYXQoeF9icmVha3MsIHNjaWVudGlmaWM9RkFMU0UpCgpnZ3Bsb3QoUGxvdF9BdXRfRnVuX3ksIGFlcyh4PWxhZywgeT1hY2YpKSsKICBnZW9tX3NlZ21lbnQoYWVzKHg9bGFnLCB5PXJlcCgwLGxlbmd0aChsYWcpKSwgeGVuZD1sYWcsIHllbmQ9YWNmKSwgbGluZXdpZHRoPTEsIGNvbD0iYmxhY2siKSArCiAgIyBnZW9tX2NvbChtYXBwaW5nPU5VTEwsIGRhdGE9TlVMTCwgcG9zaXRpb249ImRvZGdlIiwgd2lkdGg9MC4xLCBjb2w9ImJsYWNrIiwgaW5oZXJpdC5hZXM9VFJVRSkrCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD0tY2lfOTAsIGNvbG9yPSJDSV85MCIpLCBzaG93LmxlZ2VuZD1UUlVFLCBsdHk9MykgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9Y2lfOTAsIGNvbG9yPSJDSV85MCIpLCBsdHk9MykgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9Y2lfOTUsIGNvbG9yPSJDSV85NSIpLCBzaG93LmxlZ2VuZD1UUlVFLCBsdHk9NCkrCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD0tY2lfOTUsIGNvbG9yPSJDSV85NSIpLCBsdHk9NCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9LWNpXzk5LCBjb2xvcj0iQ0lfOTkiKSwgc2hvdy5sZWdlbmQ9VFJVRSwgbHR5PTQpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PWNpXzk5LCBjb2xvcj0iQ0lfOTkiKSwgbHR5PTQpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZT0ibGFnIiwgYnJlYWtzPXhfYnJlYWtzLCBsYWJlbD14X2xhYnMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZT0iYWNmIHZhbHVlIiwgYnJlYWtzPXdhaXZlcigpLCBsYWJlbHM9TlVMTCwKICAgICAgICAgICAgICAgICAgICAgc2VjLmF4aXM9c2VjX2F4aXMofi4sIGJyZWFrcz13YWl2ZXIoKSwgbGFiZWxzPXdhaXZlcigpKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJDb25mLiBJbnRlci4iLCBsYWJlbHM9YygiOTAlIiwiOTUlIiwiOTklIiksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcz1jKENJXzkwPSJncmVlbiIsIENJXzk1PSJibHVlIiwgQ0lfOTk9InJlZCIpKSArCiAgZ2d0aXRsZSh0aXRsZV9jb250ZW50KSArCiAgbGFicyhzdWJ0aXRsZT1zdWJ0aXRsZV9jb250ZW50LCBjYXB0aW9uPWNhcHRpb25fY29udGVudCkgKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT04KSwgCiAgICAgICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9IDAuNSwgc2l6ZT03LjUpLAogICAgICAgIHBsb3QuY2FwdGlvbj1lbGVtZW50X3RleHQoaGp1c3Q9MS4wKSwKICAgICAgICBsZWdlbmQua2V5LndpZHRoPXVuaXQoMC44LCJjbSIpLCBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpCmBgYApgYGB7cn0KIyBUaGUgcGFydGlhbCBhdXRvY29ycmVsb2dyYW0gb2YgdGhlIHJlbWFpbmRlcnMKRGF0YV9kZiA8LSBDYW1yeV9Ucm5TX2RmCnkgPC0gQVVUT0FSSU1BX3lfQUlDW1sicmVzaWR1YWxzIl1dClQgPC0gbGVuZ3RoKHkpCiMgbWF4bGFnIDwtIGNlaWxpbmcoMTAqbG9nMTAoVCkpICAgICMgRGVmYXVsdAojIG1heGxhZyA8LSBjZWlsaW5nKHNxcnQobikrNDUpICAgICAjIEJveC1KZW5raW5zCm1heGxhZyA8LSBjZWlsaW5nKG1pbigxMCwgVC80KSkgICAgICMgSHluZG1hbiAoZm9yIGRhdGEgd2l0aG91dCBzZWFzb25hbGl0eSkKIyBtYXhsYWcgPC0gY2VpbGluZyhtaW4oMioxMiwgVC81KSkgIyBIeW5kbWFuIChmb3IgZGF0YSB3aXRoIHNlYXNvbmFsaXR5KSAKIyBodHRwczovL3JvYmpoeW5kbWFuLmNvbS9oeW5kc2lnaHQvbGp1bmctYm94LXRlc3QvClBhcnRfQXV0X0Z1bl95IDwtIHBhY2YoeSwgbGFnLm1heD1tYXhsYWcsIHBsb3Q9RkFMU0UpCmNpXzkwIDwtIHFub3JtKCgxKzAuOTApLzIpL3NxcnQoVCkKY2lfOTUgPC0gcW5vcm0oKDErMC45NSkvMikvc3FydChUKQpjaV85OSA8LSBxbm9ybSgoMSswLjk5KS8yKS9zcXJ0KFQpClBsb3RfUGFydF9BdXRfRnVuX3kgPC0gZGF0YS5mcmFtZShsYWc9UGFydF9BdXRfRnVuX3kkbGFnLCBwYWNmPVBhcnRfQXV0X0Z1bl95JGFjZikKdGl0bGVfY29udGVudCA8LSBicXVvdGUoYXRvcCgiUGFydGlhbCBBdXRvY29ycmVsb2dyYW0gb2YgdGhlIFJlc2lkdWFscyBvZiB0aGUgQVJJTUEgbW9kZWwgZm9yIHRoZSBSZW1haW5kZXJzIGluIHRoZSBTVEwgRGVjb21wLiBmb3IgdGhlIExvZyBCb3gtQ294IFRyYW5zZi4gb2YgdGhlIFRyYWluaW5nIFNldCBmcm9tICIsIC4oRmlyc3RfRGF0ZSksICIgdG8gIiwgLihMYXN0X0RhdGUpKSkKc3VidGl0bGVfY29udGVudCA8LSBicXVvdGUocGFzdGUoIlBhdGggbGVuZ3RoICIsIC4oVCksICIgc2FtcGxlIHBvaW50cy4gTGFncyAiLCAuKG1heGxhZykpKQpjYXB0aW9uX2NvbnRlbnQgPC0gIkF1dGhvcjogTWF0dGVvIENoaWFjY2hpYSIKeF9uYW1lIDwtIGJxdW90ZSgibGFncyIpCnhfYnJlYWtzX251bSA8LSBtYXhsYWcKeF9iaW53aWR0aCA8LSAxCnhfYnJlYWtzIDwtIFBhcnRfQXV0X0Z1bl95JGxhZwp4X2xhYnMgPC0gZm9ybWF0KHhfYnJlYWtzLCBzY2llbnRpZmljPUZBTFNFKQpnZ3Bsb3QoUGxvdF9QYXJ0X0F1dF9GdW5feSwgYWVzKHg9bGFnLCB5PXBhY2YpKSsKICBnZW9tX3NlZ21lbnQoYWVzKHg9bGFnLCB5PXJlcCgwLGxlbmd0aChsYWcpKSwgeGVuZD1sYWcsIHllbmQ9cGFjZiksIGxpbmV3aWR0aD0xLCBjb2w9ImJsYWNrIikgKwogICMgZ2VvbV9jb2wobWFwcGluZz1OVUxMLCBkYXRhPU5VTEwsIHBvc2l0aW9uPSJkb2RnZSIsIHdpZHRoPTAuMSwgY29sPSJibGFjayIsIGluaGVyaXQuYWVzPVRSVUUpKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9LWNpXzkwLCBjb2xvcj0iQ0lfOTAiKSwgc2hvdy5sZWdlbmQ9VFJVRSwgbHR5PTMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PWNpXzkwLCBjb2xvcj0iQ0lfOTAiKSwgbHR5PTMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PWNpXzk1LCBjb2xvcj0iQ0lfOTUiKSwgc2hvdy5sZWdlbmQ9VFJVRSwgbHR5PTQpKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9LWNpXzk1LCBjb2xvcj0iQ0lfOTUiKSwgbHR5PTQpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PS1jaV85OSwgY29sb3I9IkNJXzk5IiksIHNob3cubGVnZW5kPVRSVUUsIGx0eT00KSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD1jaV85OSwgY29sb3I9IkNJXzk5IiksIGx0eT00KSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9ImxhZyIsIGJyZWFrcz14X2JyZWFrcywgbGFiZWw9eF9sYWJzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWU9InBhY2YgdmFsdWUiLCBicmVha3M9d2FpdmVyKCksIGxhYmVscz1OVUxMLAogICAgICAgICAgICAgICAgICAgICBzZWMuYXhpcz1zZWNfYXhpcyh+LiwgYnJlYWtzPXdhaXZlcigpLCBsYWJlbHM9d2FpdmVyKCkpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkNvbmYuIEludGVyLiIsIGxhYmVscz1jKCI5MCUiLCI5NSUiLCI5OSUiKSwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzPWMoQ0lfOTA9InJlZCIsIENJXzk1PSJibHVlIiwgQ0lfOTk9ImdyZWVuIikpICsKICBnZ3RpdGxlKHRpdGxlX2NvbnRlbnQpICsKICBsYWJzKHN1YnRpdGxlPXN1YnRpdGxlX2NvbnRlbnQsIGNhcHRpb249Y2FwdGlvbl9jb250ZW50KSArCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MC41LCBzaXplPTgpLCAKICAgICAgICBwbG90LnN1YnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0gMC41LCBzaXplPTguNSksCiAgICAgICAgcGxvdC5jYXB0aW9uPWVsZW1lbnRfdGV4dChoanVzdD0xLjApLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGg9dW5pdCgwLjgsImNtIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKYGBgCklsIG51bWVybyBkaSBwaWNjaGkgY29ycmlzcG9uZGVudGkgYWkgbGFncyBwb3NpdGl2aSBjaGUgYXR0cmF2ZXJzYW5vIGxlIGxpbmVlIGRpIGNvbmZpZGVuemEgw6ggbnVsbG8uICBjb25mZXJtYW5kbyBpbCByaXN1bHRhdG8gb3R0ZW51dG8gZGFsCipMQiB0ZXN0Ki4KCioqQ09WSUQtMTkqKjogTGEgcGFuZGVtaWEgZGkgKkNPVklELTE5KiBoYSBjYXVzYXRvIHVuYSBzZXJpZSBkaSBjYW1iaWFtZW50aSBuZWxsJ2Vjb25vbWlhLCBuZWxsYSBzb2NpZXTDoCBlIG5lbGxhIHZpdGEgcXVvdGlkaWFuYSBkZWxsZSBwZXJzb25lLiBDacOyIGhhIGNvbXBvcnRhdG8gdW5hIHNlcmllIGRpIGNvbnNlZ3VlbnplIGNoZSBwb3Nzb25vIGluZmx1ZW56YXJlIGkgbW9kZWxsaSBkZWxsZSAqdGltZSBzZXJpZXMqLiBBZCBlc2VtcGlvLCBsZSBjaGl1c3VyZSBmb3J6YXRlIGRlbGxlIGF0dGl2aXTDoCBlY29ub21pY2hlLCBsZSBpbnRlcnJ1emlvbmkgbmVsbGEgY2F0ZW5hIGRpIGFwcHJvdnZpZ2lvbmFtZW50byBlIGxlIGZsdXR0dWF6aW9uaSBuZWkgY29tcG9ydGFtZW50aSBkaSBzcGVzYSBkZWkgY29uc3VtYXRvcmkgcG9zc29ubyBpbmZsdWlyZSBzdWkgZGF0aSBjaGUgYWxpbWVudGFubyBxdWVzdGkgbW9kZWxsaS4KCk1vZGlmaWNhcmUgaWwgdmFsb3JlIGRlbCAqbm9pc2UqIGRpIHVuIG1vZGVsbG8gZGkgKnRpbWUgc2VyaWVzKiBwdcOyIGFpdXRhcmUgYSB0ZW5lciBjb250byBkaSBxdWVzdGUgZmx1dHR1YXppb25pIGltcHJldmlzdGUgZSBkZWxsZSBpbmNlcnRlenplIGNhdXNhdGUgZGFsbGEgcGFuZGVtaWEuIENpw7IgcHXDsiBlc3NlcmUgcGFydGljb2xhcm1lbnRlIGltcG9ydGFudGUgcGVyIGxhIHByZXZpc2lvbmUgZGVsbGEgZG9tYW5kYSBkaSBtZXJjYXRvLCBwZXIgbGEgdmFsdXRhemlvbmUgZGVsbGUgcmlzb3JzZSBmaW5hbnppYXJpZSBuZWNlc3NhcmllIGUgcGVyIGxhIHBpYW5pZmljYXppb25lIGEgbHVuZ28gdGVybWluZS4gVHV0dGF2aWEsIGxhIG1vZGlmaWNhIGRlbCB2YWxvcmUgZGVsICpub2lzZSogZGV2ZSBlc3NlcmUgc3VwcG9ydGF0YSBkYSB1bmEgc29saWRhIGFuYWxpc2kgZGVpIGRhdGkgZSBkZWxsYSBzaXR1YXppb25lIGF0dHVhbGUgcGVyIGdhcmFudGlyZSBjaGUgbGUgcHJldmlzaW9uaSBzaWFubyBpbCBwacO5IGFjY3VyYXRlIHBvc3NpYmlsZS4KClNlY29uZG8gaSBkYXRpIGRlbGwnKkFzc29jaWF6aW9uZSBOYXppb25hbGUgZGVpIFJpdmVuZGl0b3JpIGRpIEF1dG8gKE5BREEpKiwgbGUgdmVuZGl0ZSBkaSBhdXRvIG5lZ2xpIFN0YXRpIFVuaXRpIHNvbm8gZGltaW51aXRlIGRlbCAxNCw2JSBuZWwgMjAyMCByaXNwZXR0byBhbGwnYW5ubyBwcmVjZWRlbnRlLCBjb24gdW4gdG90YWxlIGRpIGNpcmNhIDE0LDUgbWlsaW9uaSBkaSBhdXRvIHZlbmR1dGUgcmlzcGV0dG8gYWkgMTcgbWlsaW9uaSBkaSBhdXRvIHZlbmR1dGUgbmVsIDIwMTkuCgpMYSBwYW5kZW1pYSBoYSBhdnV0byB1biBpbXBhdHRvIHNpZ25pZmljYXRpdm8gc3VsIHNldHRvcmUgYXV0b21vYmlsaXN0aWNvIGRlZ2xpICpTdGF0aSBVbml0aSosIGNvbiBtb2x0ZSBmYWJicmljaGUgZSBjb25jZXNzaW9uYXJpZSBjaGUgaGFubm8gY2hpdXNvIHRlbXBvcmFuZWFtZW50ZSBkdXJhbnRlIGxhIHByaW1hIG9uZGF0YSBkZWxsYSBwYW5kZW1pYS4gSW5vbHRyZSwgbGEgc2l0dWF6aW9uZSBlY29ub21pY2EgaW5jZXJ0YSBoYSBwb3J0YXRvIG1vbHRpIGNvbnN1bWF0b3JpIGEgcG9zdGljaXBhcmUgbCdhY3F1aXN0byBkaSB1bidhdXRvIG8gYSBvcHRhcmUgcGVyIG9wemlvbmkgZGkgdHJhc3BvcnRvIGFsdGVybmF0aXZlIGNvbWUgaWwgY2FyIHNoYXJpbmcgbyBpbCBiaWtlIHNoYXJpbmcuCgpUdXR0YXZpYSwgbGUgdmVuZGl0ZSBkaSBhdXRvIG5lZ2xpIFN0YXRpIFVuaXRpIGhhbm5vIG1vc3RyYXRvIHVuYSByaXByZXNhIG5lbGxhIHNlY29uZGEgbWV0w6AgZGVsIDIwMjAuClZlZGlhbW8gYWRlc3NvIGlsICpsaW5lcGxvdCogcmVsYXRpdm8gYWkgcmVzaWR1aSBkZWwgbW9kZWxsbyAqQVJJTUEqLgpgYGB7cn0KYXV0b3Bsb3QoIEFVVE9BUklNQV95X0FJQ1tbInJlc2lkdWFscyJdXSkKYGBgCkRhbCAqbGluZXBsb3QqIHNpIG5vdGEgaW1tZWRpYXRhbWVudGUgbGEgcHJlc2VuemEgZGkgdW4gdmFsb3JlIGFub21hbG8uClBlciBwb3RlciB0cmF0dGFyZSBtZWdsaW8sIHF1aW5kaSwgaWwgKm5vaXNlKiBlIHBvdGVyIGVmZmV0dGl2YW1lbnRlIGFmZmVybWFyZSBkaSBhdmVyZSB1biBydW1vcmUgY2hlIG5vbiBkaXBlbmRlIGRhIG5lc3N1bmEgY2F1c2EgZXN0ZXJuYSwgbW9kaWZpY2hpYW1vIGlsIHZhbG9yZSBkaSBwaWNjbyBwacO5IGJhc3NvIHRyYW1pdGUgdW4nKmludGVycG9sYXppb25lIGxpbmVhcmUqLgpgYGB7cn0KQ2FtcnlfVHJuU19kZiA8LSBhZGRfY29sdW1uKENhbXJ5X1RyblNfZGYsIHN0bF9yZXNpZHVhbHM9QVVUT0FSSU1BX3lfQUlDW1sicmVzaWR1YWxzIl1dKQojQ292aWQgdmFsdWUgbW9kaWZ5CnkgPC0gQVVUT0FSSU1BX3lfQUlDW1sicmVzaWR1YWxzIl1dCm1pbl9jb3ZpZCA8LSBtaW4oeSkKbWluX2NvdmlkX2luZGV4IDwtIHdoaWNoLm1pbih5KQptb250aF95ZWFyX21pbl9jb3ZpZCA9IHNwcmludGYoIiVzLyVzIiwgQ2FtcnlfVHJuU19kZiRNb250aFttaW5fY292aWRfaW5kZXhdLCBDYW1yeV9Ucm5TX2RmJFllYXJbbWluX2NvdmlkX2luZGV4XSkKY2F0KG1vbnRoX3llYXJfbWluX2NvdmlkKQp5W3kgPT0gbWluX2NvdmlkXSA8LSAwCnlbbWluX2NvdmlkX2luZGV4XSA8LSAoKHlbbWluX2NvdmlkX2luZGV4KzFdIC0gIHlbbWluX2NvdmlkX2luZGV4LTFdKSAvIDIpICsgIHlbbWluX2NvdmlkX2luZGV4LTFdCnlfcmVtX0FSSU1BX3Jlc2lkdWFscyA8LSB5CmBgYApVbmEgdm9sdGEgbW9kaWZpY2F0byBpbCB2YWxvcmUgcmVsYXRpdm8gYWwgY3JvbGxvIGRlbGxlIHZlbmRpdGUgYSBjYXVzYSBkZWxsYSBwYW5kZW1pYSBkZWwgQ09WSUQtMTksIHNpIHByb2NlZGUgY29uIGwnYW5hbGlzaSBkZWkgcmVzaWR1aSwgY2VyY2FuZG8gZGkgdmVyaWZpY2FyZSBxdWFsZSBwb3NzYSBlc3NlcmUgbGEgZGlzdHJpYnV6aW9uZSBnZW5lcmF0cmljZS4KClBlciBvdHRlbmVyZSBpbmZvcm1hemlvbmkgc3VsbGEgZGlzdHJpYnV6aW9uZSBjaGUgbWVnbGlvIHJhcHByZXNlbnRhIGkgcmVzaWR1aSBkZWwgbW9kZWxsbyBBUk1BLCBwb3NzaWFtbyBjb25zaWRlcmFyZSBpbCBncmFmaWNvIGRpICoqQ3VsbGVuLUZyZXkqKiBkZWxsYSAqc2tld25lc3MqIGUgZGVsbGEgKmt1cnRvc2lzKiBkZWkgZGF0aS4gCmBgYHtyfQp5IDwtIGFzLnZlY3Rvcih5X3JlbV9BUklNQV9yZXNpZHVhbHMpCmRlc2NkaXN0KHksIGRpc2NyZXRlPUZBTFNFLCBtZXRob2Q9InNhbXBsZSIsIGdyYXBoPVRSVUUsIGJvb3Q9MTAwMCkKYGBgCk5lbCBncmFmaWNvIGlsIHB1bnRvIGJsdSByYXBwcmVzZW50YSBsYSBjb3BwaWEgKnNrZXduZXNzLWt1cnRvc2lzKiBkZWkgZGF0aSBvc3NlcnZhdGkgKGNpb8OoIGxhICpza2V3bmVzcyogZSBsYSAqa3VydG9zaXMqIGRlaSByZXNpZHVpIGRlbCBtb2RlbGxvICpBUk1BKiksIGUgaSBwdW50aSBhcmFuY2lvbmkgcmFwcHJlc2VudGFubyBsZSBjb3BwaWUgc2tld25lc3Mta3VydG9zaXMgZGkgMTAwMCBjYW1waW9uYW1lbnRpIGRlaSBkYXRpIG9zc2VydmF0aSBvdHRlbnV0aSBjb24gaWwgbWV0b2RvIGRlbCAqYm9vdHN0cmFwLiogU3VsIGdyYWZpY28gc29ubyByaXBvcnRhdGkgY29uIGRpdmVyc2kgc2ltYm9saSBlIGxpbmVlIChjb21lIHNwaWVnYXRvIG5lbGxhIGxlZ2VuZGEpIGxhICpza2V3bmVzcyogZSBsYSAqa3VydG9zaXMqIGRpIGFsY3VuZSBkaXN0cmlidXppb25pIG5vdGUsIGNvbWUgbGEgbm9ybWFsZSwgbGEgZ2FtbWEsIGwndW5pZm9ybWUsIGVjYy4gSSBwdW50aSBkaSBib290c3RyYXAgc29ubyB2aWNpbmkgYWxsZSBkaXN0cmlidXppb25pICpsb2dpc3RpY2EqIGUgKlQtU3R1ZGVudCouIFBlcnRhbnRvLCBxdWVzdGUgZHVlIGRpc3RyaWJ1emlvbmkgcG90cmViYmVybyBlc3NlcmUgdGVzdGF0ZSBwZXIgdmFsdXRhcmUgcXVhbGUgbWVnbGlvIHJhcHByZXNlbnRpIGkgZGF0aS4KQ2FsY29saWFtbyBhbmNoZSBpIHZhbG9yaSBkaSAqc2tld25lc3MqIGUgKmt1cnRvc2lzKiBjb24gcmVsYXRpdmkgaW50ZXJ2YWxsaSBkaSBjb25maWRlbnphLgpgYGB7cn0KeSA8LSB5X3JlbV9BUklNQV9yZXNpZHVhbHMKeV9za2V3IDwtIERlc2NUb29sczo6U2tldyh5LCB3ZWlnaHRzPU5VTEwsIG5hLnJtPVRSVUUsIG1ldGhvZD0yLCBjb25mLmxldmVsPTAuOTAsIGNpLnR5cGU9ImJjYSIsIFI9MTAwMCkgCnNob3coeV9za2V3KQpgYGAKYGBge3J9CnkgPC0geV9yZW1fQVJJTUFfcmVzaWR1YWxzCnlfa3VydCA8LSBEZXNjVG9vbHM6Okt1cnQoeSwgd2VpZ2h0cz1OVUxMLCBuYS5ybT1UUlVFLCBtZXRob2Q9MiwgY29uZi5sZXZlbD0wLjkwLCBjaS50eXBlPSJiY2EiLCBSPTEwMDApIApzaG93KHlfa3VydCkKYGBgCk5vdGlhbW8gY2hlIGxhICpza2V3bmVzcyogbnVsbGEgcmljYWRlIGFsbCdpbnRlcm5vIGRlbGwnaW50ZXJ2YWxsbyBkaSBjb25maWRlbnphIGRlbCA5MCUsIHBvc3NpYW1vIHF1aW5kaSBzdXBwb3JyZSBjaGUgaSByZXNpZHVpIGFiYmlhbm8gdW5hIGRpc3RyaWJ1emlvbmUgc2ltbWV0cmljYS4KUGVyIHF1YW50byByaWd1YXJkYSBsYSAqa3VydG9zaXMqIHBvc3NpYW1vIHN1cHBvcnJlIGNoZSBsYSBkaXN0cmlidXppb25lIHNpYSBsZXB0b2N1cnRpY2EgbWEgbm9uIHB1w7IgZXNzZXJlIHVuYSBkaXN0cmlidXppb25lIGxvZ2lzdGljYSBlc3NlbmRvIGlsIHZhbG9yZSBkZWwgc3VvIGVjY2Vzc28gZGkgKmt1cnRvc2lzKiAoJFxmcmFjezZ9ezV9JCkgbm9uIHJpZW50cmFudGUgYWxsJ2ludGVybm8gZGVsbCdpbnRlcnZhbGxvIGRpIGNvbmZpZGVuemEgY2FsY29sYXRvLgoKRXNlZ3VpYW1vIGlub2x0cmUgaSB0ZXN0IGRpICpnYXVzc2lhbml0w6AqOgoKLSAqU2hhcGlyby1XaWxrcyoKLSAqRCdBZ29zdGluby1QZWFyc29uKgotICpBbmRlcnNvbi1EYXJsaW5nKgotICpKYXJxdWUtQmVyYSoKCmBgYHtyfQojIFNoYXBpcm8tV2lsa3MgKCpTVyopIHRlc3QuCnkgPC0geV9yZW1fQVJJTUFfcmVzaWR1YWxzCnlfU1cgPC0gc2hhcGlyby50ZXN0KHkpCnNob3coeV9TVykKIyBEJ0Fnb3N0aW5vIFBlYXJzb24gKCpEUCopIHRlc3QuCnkgPC0geV9yZW1fQVJJTUFfcmVzaWR1YWxzCnlfRFAgPC0gZGFnb1Rlc3QoeSkKc2hvdyh5X0RQKQojIEFuZGVyc29uLURhcmxpbmcgKEFEKSB0ZXN0Lgp5IDwtIHlfcmVtX0FSSU1BX3Jlc2lkdWFscwp5X0FEIDwtIGFkLnRlc3QoeSkKc2hvdyh5X0FEKQojIEphcnF1ZS1CZXJhICgqSkIqKSB0ZXN0Lgp5IDwtIHlfcmVtX0FSSU1BX3Jlc2lkdWFscwp5X0pCIDwtIGphcnF1ZS5iZXJhLnRlc3QoeSkKc2hvdyh5X0pCKQpgYGAKVHV0dGkgaSB0ZXN0IHJlc2l0dWlzY29ubyB1biBmb3J0ZSByaWdldHRvIGRlbGwnaXBvdGVzaSBudWxsYSBkaSBnYXVzc2lhbml0w6AuCgpTaSBwYXNzYSwgcXVpbmRpLCBhbGxhIHN0aW1hIGRlaSBwYXJhbWV0cmkgZGVsbGEgZGlzdHJpYnV6aW9uZSAgKlQtU3R1ZGVudCoKUHJlbGltaW5hcm1lbnRlIGVmZmV0dHVpYW1vIGxlIHNlZ3VlbnRpIG9wZXJhemlvbmk6CgotIFN0YW5kYXJkaXp6aWFtbyBpIHJlc2lkdWkgb3Z2ZXJvIHRyYXNmb3JtYXJsaSBpbiBtb2RvIGNoZSBhYmJpYW5vIG1lZGlhIHplcm8gZSBkZXZpYXppb25lIHN0YW5kYXJkIHBhcmkgYSB1bm8KLSBDYWxjb2xpYW1vIGkgcXVhbnRpbGkgZW1waXJpY2kgb3Z2ZXJvIGlkZW50aWZpY2hpYW1vIGkgdmFsb3JpIGNoZSBkaXZpZG9ubyBsYSBkaXN0cmlidXppb25lIGRlaSByZXNpZHVpIGluIHBlcmNlbnR1YWxpIHVndWFsaS4KLSBDYWxjb2xpYW1vIGxhIGRpc3RyaWJ1emlvbmUgZW1waXJpY2Egb3Z2ZXJvIHVuYSBzdGltYSBkZWxsYSBkaXN0cmlidXppb25lIHJlYWxlIGRlaSByZXNpZHVpLgotIENhbGNvbGlhbW8gbGEgcHJvYmFiaWxpdMOgIGVtcGlyaWNhLgoKQ2nDsiB2ZXJyw6AgdXNhdG8gcGVyIGNvbmZyb250YXJlIGxlIGNhcmF0dGVyaXN0aWNoZSBkZWxsYSBkaXN0cmlidXppb25lIGVtcGlyaWNhIGRlaSByZXNpZHVpIGNvbiBsZSBjYXJhdHRlcmlzdGljaGUgZGVsYSBkaXN0cmlidXppb25lIGNoZSBzdGltZXJlbW8gbGkgYWJiaWEgZ2VuZXJhdGkuCmBgYHtyfQp6IDwtIGFzLnZlY3Rvcih5X3JlbV9BUklNQV9yZXNpZHVhbHMpCnpfc3QgPC0gKDEvc2QoeikpKmFzLnZlY3Rvcih6LW1lYW4oeikpICMgV2Ugc3RhbmRhcmRpemUgdGhlIHJlc2lkdWFscyBvZiB0aGUgQVJJTUEgbW9kZWwuCnpfc3RfcWVtcCA8LSBxZW1wKHBwb2ludHMoel9zdCksIHpfc3QpICMgVGhlIGVtcGlyaWNhbCBxdWFudGlsZXMgb2YgdGhlIHJlc2lkdWFscy4Kel9zdF9kZW1wIDwtIGRlbXAoel9zdF9xZW1wLCB6X3N0KSAgICAgIyBUaGUgZW1waXJpY2FsIHByb2JhYmlsaXR5IGRlbnNpdHkgb2YgdGhlIHJlc2lkdWFscy4Kel9zdF9wZW1wIDwtIHBlbXAoel9zdF9xZW1wLCB6X3N0KSAgICAgIyBUaGUgZW1waXJpY2FsIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVzaWR1YWxzLiAgCnggPC0gel9zdF9xZW1wCnlfZCA8LSB6X3N0X2RlbXAKeV9wIDwtIHpfc3RfcGVtcApoaXN0KHpfc3QsIGNvbD0iY3lhbiIsIGJvcmRlcj0iYmxhY2siLCB4bGltPWMoeFsxXS0xLjAsIHhbbGVuZ3RoKHgpXSsxLjApLCB5bGltPWMoMCwgeV9kW2xlbmd0aCh5KV0rMC43NSksIAogICAgIGZyZXE9RkFMU0UsIG1haW49IkRlbnNpdHkgSGlzdG9ncmFtIGFuZCBFbXBpcmljYWwgRGVuc2l0eSBGdW5jdGlvbiBvZiB0aGUgU3RhbmRhcmRpemVkIFJlc2lkdWFscyBvZiB0aGUgCiAgICAgQVJJTUEgbW9kZWwiLCAKICAgICB4bGFiPSJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIiwgeWxhYj0iSGlzdG9ncmFtIFZhbHVlcytEZW5zaXR5IEZ1bmN0aW9uIikKbGluZXMoeCwgeV9kLCBsd2Q9MiwgY29sPSJkYXJrYmx1ZSIpCmBgYApEZWZpbmlhbW8gbCdhZGF0dGFtZW50byBhIHVuYSAqU3R1ZGVudCouClBlciBwcm9jZWRlcmUgZGVmaW5pYW1vIG9yYSBsZSBmdW56aW9uaSBjYXJhdHRlcmlzdGljaGUgZGkgdW5hIFN0dWRlbnQgZ2VuZXJhbGl6emF0YSwgbm9uIGRpc3BvbmliaWxpIHN1bGxlIGxpYnJlcmllIGRpIFIsIG1hIGNvc3RydWliaWxpIGEgcGFydGlyZSBkYSB0cmFzZm9ybWF6aW9uaSBzdGFuZGFyZCBkZWxsZSBmdW56aW9uaSBhbmFsb2doZSBkZWxsYSBkaXN0cmlidXppb25lIHN0YW5kYXJkIGRpIFN0dWRlbnQgZGlzcG9uaWJpbGkgc3UgUjoKCi0gTGEgZnVuemlvbmUgKmR0X2xzKiByZXN0aXR1aXNjZSBpbCB2YWxvcmUgZGVsbGEgZnVuemlvbmUgZGkgZGVuc2l0w6AgZGkgcHJvYmFiaWxpdMOgIGRlbGxhIGRpc3RyaWJ1emlvbmUgZ2VuZXJhbGl6emF0YSBkaSBTdHVkZW50IGEgcGFydGlyZSBkYWxsYSBkZW5zaXTDoCBkdCBkZWxsYSBkaXN0cmlidXppb25lIGRpICpTdHVkZW50IFN0YW5kYXJkKgotIExhIGZ1bnppb25lICpwdF9scyogcmVzdGl0dWlzY2UgaWwgdmFsb3JlIGRlbGxhIGZ1bnppb25lIGRpIGRlbnNpdMOgIGN1bXVsYXRpdmEgZGVsbGEgZGlzdHJpYnV6aW9uZSBnZW5lcmFsaXp6YXRhIGRpIFN0dWRlbnQgYSBwYXJ0aXJlIGRhbGxhIGRlbnNpdMOgIGN1bXVsYXRhIHB0IGRlbGxhIGRpc3RyaWJ1emlvbmUgZGkgKlN0dWRlbnQgU3RhbmRhcmQqCi0gTGEgZnVuemlvbmUgKnF0X2xzKiByZXN0aXR1aXNjZSBpbCB2YWxvcmUgZGVsbGEgZnVuemlvbmUgZGkgZGVuc2l0w6AgY3VtdWxhdGl2YSBpbnZlcnNhIGRlbGxhIGRpc3RyaWJ1emlvbmUgZ2VuZXJhbGl6emF0YSBkaSBTdHVkZW50IGEgcGFydGlyZSBkYWxsYSBkZW5zaXTDoCBjdW11bGF0YSBpbnZlcnNhIHF0IGRlbGxhIGRpc3RyaWJ1emlvbmUgZGkgKlN0dWRlbnQgU3RhbmRhcmQqCi0gTGEgZnVuemlvbmUgKnJ0X2xzKiBnZW5lcmEgdW4gdmV0dG9yZSBkaSB2YXJpYWJpbGkgY2FzdWFsaSBkaXN0cmlidWl0aSBzZWNvbmRvIGxhIGRpc3RyaWJ1emlvbmUgZ2VuZXJhbGl6emF0YSBkaSBTdHVkZW50IGEgcGFydGlyZSBkYSB1biB2ZXR0b3JlIGRpIHZhcmlhYmlsaSBjYXN1YWxpIGNoZSBzZWd1b25vIGxhIGRpc3RyaWJ1emlvbmUgZGkgKlN0dWRlbnQgU3RhbmRhcmQqCgpM4oCZaW50cm9kdXppb25lIGRpIHF1ZXN0ZSBmdW56aW9uaSBjb25zZW50ZSBkaSBzdGltYXJlIGkgcGFyYW1ldHJpIGRlbGxhIGlwb3RldGljYSBkaXN0cmlidXppb25lIGdlbmVyYWxpenphdGEgZGkgKlN0dWRlbnQqIGNoZSBnZW5lcmEgaSByZXNpZHVpIG1lZGlhbnRlIGxhIGZ1bnppb25lIGZpdGRpc3RyIGRlbGxhIGxpYnJlcmlhIE1BU1MuCmBgYHtyfQpkdF9scyA8LSBmdW5jdGlvbih4LCBtLCBzLCBkZikJMS9zKmR0KCh4LW0pL3MsIGRmKQpwdF9scyA8LSBmdW5jdGlvbihxLCBtLCBzLCBkZikgIHB0KChxLW0pL3MsIGRmKQpxdF9scyA8LSBmdW5jdGlvbihwLCBtLCBzLCBkZikgIHF0KHAsIGRmKSpzK20KcnRfbHMgPC0gZnVuY3Rpb24obiwgbSwgcywgZGYpICBydChuLGRmKSpzK20KYGBgCkluIHF1ZXN0byBjYXNvLCBpbCBwYXJhbWV0cm8gZGkgKmxvY2F0aW9uKiBtIGNvaW5jaWRlIGNvbiBsYSBtZWRpYSBkZWxsYSBkaXN0cmlidXppb25lICpTdHVkZW50KiBnZW5lcmFsaXp6YXRhLCBtYSBpbCBwYXJhbWV0cm8gZGkgKnNjYWxlKiBzIMOoIGRhdG8gZGEgcz0gJFxzaWdtYVxzcXJ0e1xmcmFje2RmLTJ9e2RmfX0kIGRvdmUgJFxzaWdtYSQgw6ggaWwgcGFyYW1ldHJvIGRpIGRldmlhemlvbmUgc3RhbmRhcmQgZGVsbGEgZGlzdHJpYnV6aW9uZS4gUGVydGFudG8sIHBvaWNow6kgYXNzdW1pYW1vIGNvbWUgcHVudG8gZGkgcGFydGVuemEgKmRmPTMqLCBsYSBzY2VsdGEgbmF0dXJhbGUgcGVyIGlsIHBhcmFtZXRybyBkaSAqbG9jYWN0aW9uKiDDqCAqbT0wKiBlIHBlciBpbCBwYXJhbWV0cm8gZGkgKnNjYWxlKiDDqCBzPSRcc3FydHtcZnJhY3sxfXszfX0kLgpgYGB7cn0KZml0ZGlzdF90X2xzIDwtIGZpdGRpc3Qoel9zdCwgInRfbHMiLCBzdGFydD1saXN0KG09MCwgcz1zcXJ0KDEvMyksIGRmPTMpLCBtZXRob2Q9Im1sZSIpCmZpdGRpc3RfdF9sc19tIDwtIGFzLm51bWVyaWMoZml0ZGlzdF90X2xzJGVzdGltYXRlWzFdKQpmaXRkaXN0X3RfbHNfcyA8LSBhcy5udW1lcmljKGZpdGRpc3RfdF9scyRlc3RpbWF0ZVsyXSkKZml0ZGlzdF90X2xzX2RmIDwtIGFzLm51bWVyaWMoZml0ZGlzdF90X2xzJGVzdGltYXRlWzNdKQpzdW1tYXJ5KGZpdGRpc3RfdF9scykKYGBgClNpIHRyYWNjaWEgaWwgZ3JhZmljbyBkZWxsYSBkZW5zaXTDoCBzdGltYXRhIGRlbGxhIFN0dWRlbnQgZ2VuZXJhbGl6emF0YSBpbnNpZW1lIGFsbCdpc3RvZ3JhbW1hIGUgYWxsYSBkZW5zaXTDoCBlbXBpcmljYS4KYGBge3J9Cmhpc3Qoel9zdCwgY29sPSJncmVlbiIsIGJvcmRlcj0iYmxhY2siLCB4bGltPWMoeFsxXS0yLjAsIHhbbGVuZ3RoKHgpXSsyLjApLCB5bGltPWMoMCwgeV9kW2xlbmd0aCh5X2QpXSswLjc1KSwgCiAgICAgZnJlcT1GQUxTRSwgbWFpbj0iRGVuc2l0eSBIaXN0b2dyYW0gb2YgdGhlIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMgb2YgdGhlIEFSSU1BIG1vZGVsK0VtcGlyaWNhbCAKICAgICBEZW5zaXR5K0VzdGltYXRlZCBHZW5lcmFsaXplZCBTdHVkZW50IERlbnNpdHkiLCB4bGFiPSJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIiwgeWxhYj0iRGVuc2l0eSIpCmxpbmVzKGRlbnNpdHkoel9zdCksIGx3ZD0yLCBjb2w9ImRhcmtncmVlbiIpCmxpbmVzKHgsIGR0X2xzKHgsIG09Zml0ZGlzdF90X2xzX20sIHM9Zml0ZGlzdF90X2xzX3MsIGRmPWZpdGRpc3RfdF9sc19kZiksIGx3ZD0yLCBjb2w9ImJsdWUiKQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9YygiRW1waXJpY2FsIERlbnNpdHkiLCAiRXN0aW1hdGVkIERlbnNpdHkiKSwgY29sPWMoImRhcmtncmVlbiIsICJibHVlIiksIAogICAgICAgbHR5PTEsIGx3ZD0wLjEsIGNleD0wLjgsIHguaW50ZXJzcD0wLjUwLCB5LmludGVyc3A9MC40MCwgdGV4dC53aWR0aD0yLCBzZWcubGVuPTEsIHRleHQuZm9udD00LCBib3gubHR5PTAsCiAgICAgICBpbnNldD0tMC4wMSwgYnR5PSJuIikKYGBgClNpIHRyYWNjaWEgaWwgZ3JhZmljbyAqQ0RGIChjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbikqIHBlciBjb25mcm9udGFyZSBsYSBmdW56aW9uZSBkaSBkaXN0cmlidXppb25lIGN1bXVsYXRpdmEgZW1waXJpY2EgY29uIHF1ZWxsYSBzdGltYXRhIGRhbGxhIGRpc3RyaWJ1emlvbmUgZGkgcHJvYmFiaWxpdMOgIGRpICpTdHVkZW50LiogUXVlc3RvIHBlcm1ldHRlIGRpIHZhbHV0YXJlIHNlIGxhIGRpc3RyaWJ1emlvbmUgZGkgcHJvYmFiaWxpdMOgIHN0aW1hdGEgc2kgYWRhdHRhIGJlbmUgYWkgZGF0aSBlbXBpcmljaS4KYGBge3J9CmNkZmNvbXAoZml0ZGlzdF90X2xzKQpgYGAKU2kgdHJhY2NpYW5vIGFuY2hlICpRLVEgcGxvdCogZSAqUC1QIHBsb3QqLiAKCi0gSWwgZ3JhZmljbyAqKlEtUSBwbG90KiogY29uZnJvbnRhIGkgcXVhbnRpbGkgZGVsbGEgZGlzdHJpYnV6aW9uZSBkaSBwcm9iYWJpbGl0w6AgZW1waXJpY2EgY29uIHF1ZWxsaSBkZWxsYSBkaXN0cmlidXppb25lIHRlb3JpY2EgbyBzdGltYXRhLiBRdWVzdG8gcGVybWV0dGUgZGkgdmFsdXRhcmUgc2UgbGUgZHVlIGRpc3RyaWJ1emlvbmkgc29ubyBzaW1pbGkgbyBzZSBkaWZmZXJpc2Nvbm8gc2lnbmlmaWNhdGl2YW1lbnRlIHRyYSBsb3JvLiBTZSBsZSBkdWUgZGlzdHJpYnV6aW9uaSBzb25vIHNpbWlsaSwgaSBwdW50aSBzdWwgZ3JhZmljbyAqUS1RIHBsb3QqIHNpIGFkYXR0ZXJhbm5vIGEgdW5hIHJldHRhIGRpYWdvbmFsZS4gU2UgbGUgZHVlIGRpc3RyaWJ1emlvbmkgZGlmZmVyaXNjb25vLCBpIHB1bnRpIHN1bCBncmFmaWNvIFEtUSBwbG90IHNpIGRpc2Nvc3RlcmFubm8gZGFsbGEgcmV0dGEgZGlhZ29uYWxlLgoKLSBJbCBncmFmaWNvICoqUC1QIHBsb3QqKiBjb25mcm9udGEgbGUgZnVuemlvbmkgZGkgZGlzdHJpYnV6aW9uZSBjdW11bGF0aXZhIGRlbGxhIGRpc3RyaWJ1emlvbmUgZGkgcHJvYmFiaWxpdMOgIGVtcGlyaWNhIGNvbiBxdWVsbGEgZGVsbGEgZGlzdHJpYnV6aW9uZSB0ZW9yaWNhIG8gc3RpbWF0YS4gQW5jaGUgaW4gcXVlc3RvIGNhc28sIHNlIGxlIGR1ZSBkaXN0cmlidXppb25pIHNvbm8gc2ltaWxpLCBpIHB1bnRpIHN1bCBncmFmaWNvIFAtUCBwbG90IHNpIGFkYXR0ZXJhbm5vIGEgdW5hIHJldHRhIGRpYWdvbmFsZS4gU2UgbGUgZHVlIGRpc3RyaWJ1emlvbmkgZGlmZmVyaXNjb25vLCBpIHB1bnRpIHN1bCBncmFmaWNvIFAtUCBwbG90IHNpIGRpc2Nvc3RlcmFubm8gZGFsbGEgcmV0dGEgZGlhZ29uYWxlLgoKSW4gZ2VuZXJhbGUsIGkgZ3JhZmljaSAqUS1RIHBsb3QqIGUgKlAtUCBwbG90KiB2ZW5nb25vIHV0aWxpenphdGkgcGVyIHZhbHV0YXJlIGxhIGJvbnTDoCBkaSBhZGF0dGFtZW50byBkaSB1bmEgZGlzdHJpYnV6aW9uZSB0ZW9yaWNhIG8gc3RpbWF0YSBhaSBkYXRpIGVtcGlyaWNpLiBTZSBsZSBkdWUgZGlzdHJpYnV6aW9uaSBzb25vIHNpbWlsaSwgbGEgZGlzdHJpYnV6aW9uZSB0ZW9yaWNhIG8gc3RpbWF0YSBwdcOyIGVzc2VyZSBjb25zaWRlcmF0YSB1biBidW9uIG1vZGVsbG8gcGVyIGkgZGF0aS4gU2UgbGUgZHVlIGRpc3RyaWJ1emlvbmkgZGlmZmVyaXNjb25vIHNpZ25pZmljYXRpdmFtZW50ZSwgw6ggbmVjZXNzYXJpbyBjb25zaWRlcmFyZSBsJ3V0aWxpenpvIGRpIHVuIG1vZGVsbG8gZGl2ZXJzbyBvIGRpIG1vZGlmaWNhcmUgaSBwYXJhbWV0cmkgZGVsIG1vZGVsbG8uCmBgYHtyfQpwYXIobWZyb3c9YygxLDIpKQpxcWNvbXAoZml0ZGlzdF90X2xzKQpwcGNvbXAoZml0ZGlzdF90X2xzKQpwYXIobWZyb3c9YygxLDEpKQpgYGAKU2kgdHJhY2NpYSBhbmNoZSB1biAqUS1RIHBsb3QqIHBpw7kgZGV0dGFnbGlhdG8uCmBgYHtyfQp4IDwtIHpfc3RfcWVtcApmaXRkaXN0X3RfbHNfbSA8LSBhcy5udW1lcmljKGZpdGRpc3RfdF9scyRlc3RpbWF0ZVsxXSkKZml0ZGlzdF90X2xzX3MgPC0gYXMubnVtZXJpYyhmaXRkaXN0X3RfbHMkZXN0aW1hdGVbMl0pCmZpdGRpc3RfdF9sc19kZiA8LSBhcy5udW1lcmljKGZpdGRpc3RfdF9scyRlc3RpbWF0ZVszXSkKY2FyOjpxcVBsb3QoeCwgZGlzdHJpYnV0aW9uID0idF9scyIsIG09Zml0ZGlzdF90X2xzX20sIHM9Zml0ZGlzdF90X2xzX3MsIGRmPWZpdGRpc3RfdF9sc19kZiwKICAgICAgICAgICAgbGluZT0icm9idXN0IiwgCiAgICAgICAgICAgIGNvbD1jYXJQYWxldHRlKClbMl0sIGNvbC5saW5lcz1jYXJQYWxldHRlKClbOF0sCiAgICAgICAgICAgIHBjaD0xNiwgY2V4PTAuNSwgbGFzPTEsCiAgICAgICAgICAgIG1haW49YnF1b3RlKGF0b3AoIlFRLXBsb3Qgb2YgdGhlIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMgb2YgdGhlIEFSSU1BIE1vZGVsIGZvciB0aGUgRGV0cmVuZGVkIGFuZCBEZXNlYXNvbmFsaXplZCBDb21wb25lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJvZiB0aGUgQm94LUNveCBUcmFuc2Zvcm1hdGlvbiBvZiB0aGUgTW9udGhseSBUb3lvdGEgQ2FtcnkgU2FsZXMgVGltZSBTZXJpZXMgQWdhaW5zdCB0aGUgR2VuZXJhbGl6ZWQgU3R1ZGVudCBEaXN0cmlidXRpb24gd2l0aCBMb2NhdGlvbiAiLCAuKGZpdGRpc3RfdF9sc19tKSwiLCBTY2FsZSAiLCAuKGZpdGRpc3RfdF9sc19zKSwiLCBhbmQgRGVncmVlcyBvZiBGcmVlZG9tICIsIC4oZml0ZGlzdF90X2xzX2RmKSwiLiIpKSksCiAgICAgICAgICAgIHhsYWI9YnF1b3RlKHBhc3RlKCJUaGVvcmV0aWNhbCBRdWFudGlsZXMgb2YgdGhlIEdlbmVyYWxpemVkIFN0dWRlbnQgRGlzdHJpYnV0aW9uIikpLCAKICAgICAgICAgICAgeWxhYj0iUXVhbnRpbGVzIG9mIHRoZSBFbXBpcmljYWwgRGlzdHJpYnV0aW9uIG9mIHRoZSBSZXNpZHVhbHMiKQojIGFkZGluZyB0aGUgaW50ZXJxdWFydGlsZSBsaW5lLCBjb3JyZXNwb25kaW5nIHRvIHRoZSBvcHRpb24gInF1YXJ0aWxlcyIgb2YgdGhlIHBhcmFtZXRlciBsaW5lLgpwcm9icyA8LSBjKDAuMjUsMC43NSkKcXVhbnRfeCA8LSBhcy52ZWN0b3IocXVhbnRpbGUoeCwgcHJvYnMpKQpxdWFudF90IDwtIHF0X2xzKHByb2JzLCBtPWZpdGRpc3RfdF9sc19tLCBzPWZpdGRpc3RfdF9sc19zLCBkZj1maXRkaXN0X3RfbHNfZGYpCnNsb3BlIDwtIGRpZmYocXVhbnRfeCkvZGlmZihxdWFudF90KQppbnQgPC0gcXVhbnRfeFsxXS1zbG9wZSpxdWFudF90WzFdCmFibGluZShhPWludCwgYj1zbG9wZSwgY29sPSJibGFjayIsIGx3ZD0xKQojIGFkZGluZyB0aGUgZmlyc3QgYmlzZWN0b3Igb2YgdGhlIGF4aXMgbGluZSwgY29ycmVzcG9uZGluZyB0byB0aGUgb3B0aW9uICIwLTEiIG9mIHRoZSBwYXJhbWV0ZXIgcXEubGluZS50eXBlLgphYmxpbmUoYT0wLCBiPTEsIGNvbD0iZ3JlZW4iLCBsd2Q9MSkKbGVnZW5kKCJ0b3BsZWZ0IiwKICAgICAgIGxlZ2VuZD1jKCJyb2J1c3QgcmVncmVzc2lvbiBsaW5lIiwgImludGVycXVhcnRpbGUgbGluZSIsICJ5PXggbGluZSIpLAogICAgICAgY29sPWMoInJlZCIsICJibGFjayIsICJncmVlbiIpLAogICAgICAgbHR5PTEsIGx3ZD0wLjEsCiAgICAgICBjZXg9MC44MCwgeC5pbnRlcnNwPTAuNTAsIHkuaW50ZXJzcD0wLjQwLCB0ZXh0LndpZHRoPTIsIHNlZy5sZW49MSwKICAgICAgIGluc2V0PS0wLjAxLCBidHk9Im4iKQpgYGAKQWRlc3NvIGwnaWRlYSDDqCBlc2VndWlyZSBpbCAqYm9vdHN0cmFwcGluZyogcGVyIHN0aW1hcmUgbCdpbmNlcnRlenphIG5laSBwYXJhbWV0cmkgYWRhdHRhdGkuCgpJbm5hbnppdHV0dG8sIGFkYXR0aWFtbyB1bmEgZGlzdHJpYnV6aW9uZSB0IGRpICpTdHVkZW50KiBjb24gbWVkaWEgemVybyBhbCBkYXRhc2V0IHN0YW5kYXJkaXp6YXRvICp6X3N0Ki4KClN1Y2Nlc3NpdmFtZW50ZSwgc2kgdXRpbGl6emEgbGEgZnVuemlvbmUgKmJvb3RkaXN0KiBwZXIgZ2VuZXJhcmUgMS4wMDAgY2FtcGlvbmkgKmJvb3RzdHJhcCogZGVsbGEgZGlzdHJpYnV6aW9uZSBhZGF0dGF0YSBlIHN0aW1hcmUgbCdpbmNlcnRlenphIG5lbGxlIHN0aW1lIGRlaSBwYXJhbWV0cmkuIFNpIGVzdHJhZSBpbCB2YWxvcmUgbWVkaWFubyBkZWxsZSBzdGltZSBkZWkgcGFyYW1ldHJpIGRhaSBjYW1waW9uaSBib290c3RyYXAgZSBzaSBhcnJvdG9uZGFubywgc2FsdmFuZG9saSBuZWxsYSB2YXJpYWJpbGUgKnN0dWRlbnRfemVyb19tZWFuX3BhcmFtcyouCmBgYHtyfQpmaXRkaXN0X3RfbHNfemVyb19tZWFuIDwtIGZpdGRpc3Qoel9zdCwgInRfbHMiLCBzdGFydD1saXN0KHM9c3FydCgxLzMpLCBkZj0zKSwgbWV0aG9kPSJtbGUiLCBmaXguYXJnPWxpc3QobT0wKSkKc3VtbWFyeShmaXRkaXN0X3RfbHNfemVyb19tZWFuKQojIEFnYWluLCB0aGUgKmJvb3RkaXN0KiBmdW5jdGlvbiB0aGUgbGlicmFyeSAqZml0ZGlzdHJwbHVzKiBhbGxvd3MgdG8gZXZhbHVhdGUgdGhlIHVuY2VydGFpbnR5IGluIGVzdGltYXRlZCBwYXJhbWV0ZXJzIG9mIHRoZSBmaXR0ZWQgZGlzdHJpYnV0aW9uLgpmaXRkaXN0X3RfbHNfemVyb19tZWFuX2JkIDwtIGJvb3RkaXN0KGZpdGRpc3RfdF9sc196ZXJvX21lYW4sIG5pdGVyPTEwMDApCnN1bW1hcnkoZml0ZGlzdF90X2xzX3plcm9fbWVhbl9iZCkKIyBJbiB0aGlzIGNhc2Ugd2UgZXh0cmFjdCBhbmQgcm91bmQgZGlyZWN0bHkgdGhlIG1lZGlhbiB2YWx1ZSBvZiB0aGUgcGFyYW1ldGVycyB0aWxsIHRoZSAydGggZGVjaW1hbCBkaWdpdC4gCmZpdGRpc3RfdF9sc196ZXJvX21lYW5fYmRfbWVkIDwtIGMobWVkaWFuKGZpdGRpc3RfdF9sc196ZXJvX21lYW5fYmQkZXN0aW0kcyksIG1lZGlhbihmaXRkaXN0X3RfbHNfemVyb19tZWFuX2JkJGVzdGltJGRmKSkKc2hvdyhmaXRkaXN0X3RfbHNfemVyb19tZWFuX2JkX21lZCkKc3R1ZGVudF96ZXJvX21lYW5fcGFyYW1zIDwtIHJvdW5kKGZpdGRpc3RfdF9sc196ZXJvX21lYW5fYmRfbWVkLCAyKQpzaG93KHN0dWRlbnRfemVyb19tZWFuX3BhcmFtcykKYGBgClNpIHV0aWxpenphbm8sIHF1aW5kaSwgaSBzZWd1ZW50aSB0ZXN0IHBlciB2YWx1dGFyZSBsYSBib250w6AgZGkgYWRhdHRhbWVudG8gZGVsbGEgZGlzdHJpYnV6aW9uZSBkaSBwcm9iYWJpbGl0w6Agc3RpbWF0YToKCi0gKktvbG1vZ29yb3YtU21pcm5vdiB0ZXN0KgotICpDcmFtZXItVm9uIE1pc2VzIHRlc3QqCi0gKkFuZGVyc29uLURhcmxpbmcgdGVzdCoKCioqS29sbW9nb3Jvdi1TbWlybm92IHRlc3QqKgpgYGB7cn0KIyBUaGUgS29sbW9nb3Jvdi1TbWlybm92IHRlc3QgaW4gdGhlIGxpYnJhcnkgKnN0YXRzKgpLU194X3N0X3RfbHMgPC0ga3MudGVzdCh4LCB5PSJwdF9scyIsIG09MCwgcz1zdHVkZW50X3plcm9fbWVhbl9wYXJhbXNbMV0sIGRmPXN0dWRlbnRfemVyb19tZWFuX3BhcmFtc1syXSwgYWx0ZXJuYXRpdmU9InR3by5zaWRlZCIpCnNob3coS1NfeF9zdF90X2xzKQpgYGAKKipDcmFtZXItVm9uIE1pc2VzIHRlc3QqKgpgYGB7cn0KIyBUaGUgQ3JhbWVyLVZvbiBNaXNlcyB0ZXN0IGluIHRoZSBsaWJyYXJ5ICpnb2Z0ZXN0Ki4KIyBUaGlzIGZ1bmN0aW9uIHBlcmZvcm1zIHRoZSBDcmFtZXItVm9uIE1pc2VzIHRlc3Qgb2YgZ29vZG5lc3Mtb2YtZml0IHRvIHRoZSBkaXN0cmlidXRpb24gc3BlY2lmaWVkIGJ5IHRoZSAKIyBhcmd1bWVudCBudWxsLiBJdCBpcyBhc3N1bWVkIHRoYXQgdGhlIHZhbHVlcyBpbiB4IGFyZSBpbmRlcGVuZGVudCBhbmQgaWRlbnRpY2FsbHkgZGlzdHJpYnV0ZWQgcmFuZG9tIHZhbHVlcywgCiMgd2l0aCBzb21lIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIEYuIFRoZSBudWxsIGh5cG90aGVzaXMgaXMgdGhhdCBGIGlzIHRoZSBmdW5jdGlvbiBzcGVjaWZpZWQgYnkgdGhlIAojIGFyZ3VtZW50IG51bGwsIHdoaWxlIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIGlzIHRoYXQgRiBpcyBzb21lIG90aGVyIGZ1bmN0aW9uLgpDVk1feF9zdF90X2xzIDwtIGN2bS50ZXN0KHpfc3QsIG51bGw9InB0X2xzIiwgbT0wLCBzPXN0dWRlbnRfemVyb19tZWFuX3BhcmFtc1sxXSwgZGY9c3R1ZGVudF96ZXJvX21lYW5fcGFyYW1zWzJdLCBlc3RpbWF0ZWQ9RkFMU0UpCnNob3coQ1ZNX3hfc3RfdF9scykKYGBgCioqQW5kZXJzb24tRGFybGluZyB0ZXN0KioKYGBge3J9CiMgVGhlIEFuZGVyc29uLURhcmxpbmcgdGVzdCBpbiB0aGUgbGlicmFyeSAqZ29mdGVzdCouCkFEX3hfc3RfdF9scyA8LSBhZC50ZXN0KHpfc3QsICJwdF9scyIsIG09MCwgcz1zdHVkZW50X3plcm9fbWVhbl9wYXJhbXNbMV0sIGRmPXN0dWRlbnRfemVyb19tZWFuX3BhcmFtc1syXSwgZXN0aW1hdGVkPUZBTFNFKQpzaG93KEFEX3hfc3RfdF9scykKYGBgCkRhaSAqcC12YWx1ZXMqIG90dGVudXRpIHNpIG5vdGEgY2hlIGxhIGRpc3RyaWJ1emlvbmUgZGkgKlN0dWRlbnQqIHN0aW1hdGEgc2kgYWRhdHRhIGFiYmFzdGFuemEgYmVuZSBhaSBkYXRpLCBkaSBjb25zZWd1ZW56YSBzaSB1dGlsaXp6ZXLDoCBwZXIgbGEgY3JlYXppb25lIGRlZ2xpIGludGVydmFsbGkgZGkgY29uZmlkZW56YS4KCiMjIyAyLiBGb3JlY2FzdAoKUGVyIGVzZWd1aXJlIGlsICpmb3JlY2FzdCogZGVsIG1vZGVsbG8gZXNlZ3VpYW1vIGxlIHByZWRpemlvbmkgc3Ugb2duaSBzaW5nb2xhIGNvbXBvbmVudGUgKCpyZW1haW5kZXIqLCAqdHJlbmQqLCAqc2Vhc29uYWxpdHkqKSBwZXIgcG9pIHVuaXJsZSBpbiBtYW5pZXJhIGFkZGl0aXZhLgpTaSBjYWxjb2xhbm8gaW5vbHRyZSBsZSBiYW5kZSBkaSBwcmVkaXppb25lICg4MCUgZSA5MCUpIGRlbCBydW1vcmUuCgojIyMjIFJlbWFpbmRlciBmb3JlY2FzdAoKUGVyIGVmZmV0dHVhcmUgbGEgcHJlZGl6aW9uZSBkZWwgKnJlbWFpbmRlciogc2kgdXRpbGl6emEgaWwgbW9kZWxsbyAqQVJJTUEqIHRyb3ZhdG8gaW4gcHJlY2VkZW56YS4gTGUgYmFuZGUgZGkgcHJlZGl6aW9uZSwgaW52ZWNlLCB2ZW5nb25vIGNhbGNvbGF0ZSBtZWRpYW50ZSBsJ3V0aWxpenpvIGRlaSBxdWFudGlsaSBkZWxsYSBkaXN0cmlidXppb25lIGRpICpTdHVkZW50KiBhbCA1JSwgMjAlLCA4MCUsIDk1JSBtb2x0aXBsaWNhdGkgcGVyIGxvICpzdGFuZGFyZCBlcnJvciogZGVsbGEgcHJldmlzaW9uZSBkZWwgbW9kZWxsbyAqQVJJTUEqLiAKSWwgcXVhbnRpbGUgcmFwcHJlc2VudGEgaWwgdmFsb3JlIGFsIHF1YWxlIGxhIHByb2JhYmlsaXTDoCBjdW11bGF0aXZhIGRlbGxhIGRpc3RyaWJ1emlvbmUgw6ggdWd1YWxlIGFsIGxpdmVsbG8gZGkgY29uZmlkZW56YSBkZXNpZGVyYXRvLgoKTCdlcnJvcmUgc3RhbmRhcmQgZGVsbGEgcHJldmlzaW9uZSByYXBwcmVzZW50YSBsYSBkZXZpYXppb25lIHN0YW5kYXJkIGRlbGwnZXJyb3JlIGRpIHByZXZpc2lvbmUgZGVsIG1vZGVsbG8gKkFSSU1BLiogSW4gYWx0cmUgcGFyb2xlLCByYXBwcmVzZW50YSBsYSBtaXN1cmEgZGVsbGEgZGlzcGVyc2lvbmUgZGVpIGRhdGkgaW50b3JubyBhbGxhIGxpbmVhIGRpIHByZXZpc2lvbmUuCgpNb2x0aXBsaWNhbmRvIGlsIHF1YW50aWxlIGRlbGxhIGRpc3RyaWJ1emlvbmUgZGkgKlN0dWRlbnQqIHBlciBsJ2Vycm9yZSBzdGFuZGFyZCBkZWxsYSBwcmV2aXNpb25lLCBzaSBvdHRpZW5lIHVuYSBzdGltYSBkZWxsJ2FtcGllenphIGRlbGwnaW50ZXJ2YWxsbyBkaSBjb25maWRlbnphIHBlciBsYSBwcmV2aXNpb25lLiBRdWVzdG8gdmllbmUgcXVpbmRpIHNvbW1hdG8gYWxsYSBwcmV2aXNpb25lIG1lZGlhIHBlciBvdHRlbmVyZSBsZSBiYW5kZSBpbmZlcmlvcmkgZSBzdXBlcmlvcmkgZGVsbGEgcHJldmlzaW9uZSBhbCBsaXZlbGxvIGRpIGNvbmZpZGVuemEgZGVzaWRlcmF0by4KYGBge3J9CnkgPC0geV9CQ1RfbG9nX1NUTF9yZW1haW5kZXIKQVVUT0FSSU1BX3lfQUlDIDwtIGFyaW1hKHksIG9yZGVyPWMoNCwwLDYpLCBpbmNsdWRlLm1lYW49RkFMU0UsIG1ldGhvZD0iQ1NTLU1MIikKQVVUT0FSSU1BX3lfQUlDX3ByZWQgPC0gcHJlZGljdChBVVRPQVJJTUFfeV9BSUMsIG4uYWhlYWQgPSBUc3RTX2xlbmd0aCkKeV9yZW1fcHJlZF9mb3IgPC0gZm9yZWNhc3Q6OmZvcmVjYXN0KEFVVE9BUklNQV95X0FJQywgaD1Uc3RTX2xlbmd0aCwgbGV2ZWwgPSBjKDgwLCA5NSksIGJvb3RzdHJhcCA9IFRSVUUpCmF1dG9wbG90KHlfcmVtX3ByZWRfZm9yKQp5X3JlbV9wcmVkX2Zvcl9tZWFuIDwtIHlfcmVtX3ByZWRfZm9yW1sibWVhbiJdXQp5X3Jlc19sb2dfcHJlZF9SSF9mb3JfMDgwX2xvd19pbnQgPC0gKHlfcmVtX3ByZWRfZm9yX21lYW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgK3F0X2xzKDAuMjAsIG09MCwgcz1zdHVkZW50X3plcm9fbWVhbl9wYXJhbXNbMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZj1zdHVkZW50X3plcm9fbWVhbl9wYXJhbXNbMl0pKkFVVE9BUklNQV95X0FJQ19wcmVkJHNlKQoKeV9yZXNfbG9nX3ByZWRfUkhfZm9yXzA4MF91cHBfaW50IDwtICh5X3JlbV9wcmVkX2Zvcl9tZWFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICtxdF9scygwLjgwLCBtPTAsIHM9c3R1ZGVudF96ZXJvX21lYW5fcGFyYW1zWzFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZj1zdHVkZW50X3plcm9fbWVhbl9wYXJhbXNbMl0pKkFVVE9BUklNQV95X0FJQ19wcmVkJHNlKQoKeV9yZXNfbG9nX3ByZWRfUkhfZm9yXzA5NV9sb3dfaW50IDwtICh5X3JlbV9wcmVkX2Zvcl9tZWFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICtxdF9scygwLjA1LCBtPTAsIHM9c3R1ZGVudF96ZXJvX21lYW5fcGFyYW1zWzFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZj1zdHVkZW50X3plcm9fbWVhbl9wYXJhbXNbMl0pKkFVVE9BUklNQV95X0FJQ19wcmVkJHNlKQp5X3Jlc19sb2dfcHJlZF9SSF9mb3JfMDk1X3VwcF9pbnQgPC0gKHlfcmVtX3ByZWRfZm9yX21lYW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgK3F0X2xzKDAuOTUsIG09MCwgcz1zdHVkZW50X3plcm9fbWVhbl9wYXJhbXNbMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRmPXN0dWRlbnRfemVyb19tZWFuX3BhcmFtc1syXSkqQVVUT0FSSU1BX3lfQUlDX3ByZWQkc2UpCmBgYApTaSB2aXN1YWxpenphIGluemlhbG1lbnRlIGxhIGZ1bnppb25lIGRpICpwcmV2aXNpb25lKiBjaGUgdXRpbGl6emEgaWwgbWV0b2RvICpyYW5kb20gd2FsayogZSBjYWxjb2xhIGF1dG1hdGljYSBnbGkgaW50ZXJ2YWxsaSBkaSBjb25maWRlbnphLgpgYGB7cn0Kc2FsZXNfbG9nIDwtIGFzLnZlY3Rvcihsb2coZGYkc2FsZXMpKQpzYWxlc19sb2dfdGVzdCA8LSBzYWxlc19sb2dbYygoVHJuU19sZW5ndGgrMSk6KFRyblNfbGVuZ3RoK1RzdFNfbGVuZ3RoKSldCnNhbGVzX2xvZ19zdGwgPC0gc3RsKENhbXJ5X1RyblNfdHMsIHMud2luZG93PTExLCB0LndpbmRvdyA9IDI5LCByb2J1c3QgPSBUUlVFKQpzYWxlc19sb2dfc3RsX2ZvciA8LSBmb3JlY2FzdDo6Zm9yZWNhc3Qoc2FsZXNfbG9nX3N0bCwgaD1Uc3RTX2xlbmd0aCwgbWV0aG9kPSJuYWl2ZSIsIGxldmVsPWMoODAsOTUpKQpwbG90KHNhbGVzX2xvZ19zdGxfZm9yKQphdXRvcGxvdCh0cyhsb2coZGZfQ2FtcnlfVVNfc2FsZXMkc2FsZXMpLCBzdGFydD1jKDIwMDksIDAxKSwgZW5kPWMoMjAyMiwgMTEpLCBmcmVxdWVuY3kgPSAxMiksIHNlcmllID0iVG95b3RhIENhbXJ5IHBhdGgiKSArCiAgYXV0b2xheWVyKHRzKHNhbGVzX2xvZ19zdGxfZm9yW1sibWVhbiJdXSwgc3RhcnQ9YygyMDIxLCAxMiksIGVuZD1jKDIwMjIsIDExKSwgZnJlcXVlbmN5ID0gMTIpLCBzZXJpZXM9IkZvcmVjYXN0IHdpdGggc3RsIGZ1bmMiKSArCiAgeGxhYigiTW9udGgiKSArIHlsYWIoIlNhbGVzIikgKwogIGdndGl0bGUoIkZvcmVjYXN0IFRveW90YSBDYW1yeSBzYWxlcyIpICsKICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCh0aXRsZT0iTGVnZW5kIikpCmBgYAojIyMjIFRyZW5kIGZvcmVjYXN0CgpQZXIgZWZmZXR0dWFyZSBpbCBmb3JlY2FzdCBkZWwgKnRyZW5kKiBzaSDDqCByaWNvcnNvIGEgdmFyaSBtZXRvZGk6CgotICpOYWl2ZSBmb3JlY2FzdCoKLSAqSG9sdCBXaW50ZXJzIGZvcmVjYXN0KgotICpHcmFuIG1lYW4gZm9yZWNhc3QqCgojIyMjIyBOYWl2ZSBmb3JlY2FzdApMYSB0ZWNuaWNhIGRpICpmb3JlY2FzdCBuYWl2ZSogbyAqcmFuZG9tIHdhbGsqIMOoIHVuIHNlbXBsaWNlIG1ldG9kbyBkaSBwcmV2aXNpb25lIGRlbGxlIHNlcmllIHRlbXBvcmFsaSBjaGUgYXNzdW1lIGNoZSBpbCB2YWxvcmUgZnV0dXJvIGRpIHVuYSBzZXJpZSB0ZW1wb3JhbGUgc2Fyw6AgbG8gc3Rlc3NvIGRlbCB2YWxvcmUgcGnDuSByZWNlbnRlIG9zc2VydmF0by4gRGkgY29uc2VndWVuemEgc2kgY29uc2lkZXJhIGNvbWUgKnRyZW5kKiBmdXR1cm8gaWwgY29udGludW8gY29zdGFudGUgZGVsbCd1bHRpbW8gdmFsb3JlIGRlbCAqdHJlbmQqLiAKYGBge3J9ClRTX2xlbmd0aCA8LSBUcm5TX2xlbmd0aCtUc3RTX2xlbmd0aAp5IDwtIHlfQkNUX2xvZ19TVExfZGVmX3dpbl9kY21wX3RzJHRyZW5kCnlfdHJlbmRfbmFpdmVfcHJlZCA8LSByZXAoeVtsZW5ndGgoeSldLFRzdFNfbGVuZ3RoKQp5X3RyZW5kX25haXZlX2ZvciA8LSBjKHksIHlfdHJlbmRfbmFpdmVfcHJlZCkKeCA8LSBhcy5EYXRlKGRmJGRhdGUpCnkgPC0geV90cmVuZF9uYWl2ZV9mb3IKcGxvdCh4WzE6VHJuU19sZW5ndGhdLHlbMTpUcm5TX2xlbmd0aF0sIHR5cGU9ImwiLCBsdHkgPSAxLCBsd2Q9MS4wLCBjb2w9ImJsYWNrIiwgeGF4dCA9ICJuIiwgCiAgICAgeGxpbT1jKGFzLkRhdGUoZGYkZGF0ZSlbMV0sYXMuRGF0ZShkZiRkYXRlKVtUU19sZW5ndGhdKSwKICAgICB4bGFiPSIiLCB5bGFiPSJUcmVuZCIsIAogICAgIG1haW49Ik5haXZlIEZvcmVjYXN0IG9mIHRoZSBUcmVuZCBDb21wb25lbnQgb2YgdGhlIFNUTCBEZWNvbXBvc2l0aW9uIG9mIHRoZSBMb2cgQm94LUNveCBUcmFuc2Zvcm1hdGlvbiIsCiAgICAgY2V4Lm1haW49MC45NSkKbGluZXMoeFtUcm5TX2xlbmd0aDpUU19sZW5ndGhdLCB5W1RyblNfbGVuZ3RoOlRTX2xlbmd0aF0sIHR5cGU9ImwiLCBsdHkgPSAxLCBsd2Q9MS41LCBjb2w9ImJsdWUiKQpheGlzKDEsIGF0PXhbYyhzZXEoMSxUU19sZW5ndGgsYnk9NiksVFNfbGVuZ3RoKV0sIGxhYmVscz1mb3JtYXQoeFtjKHNlcSgxLFRTX2xlbmd0aCxieT02KSxUU19sZW5ndGgpXSwgIiVZICVtIiksCiAgICAgdGljaz1UUlVFLCBjZXguYXhpcyA9IC41KQpsZWdlbmQoInRvcHJpZ2h0IiwgaW5zZXQ9YygwLjA1LDAuMCksIGxlZ2VuZD1jKCJUcmVuZCBDb21wb25lbnQiLCAiTmFpdmUgRm9yZWNhc3QiKSwgY29sPWMoImJsYWNrIiwgImJsdWUiKSwKICAgICAgIGx0eT0xLCBsd2Q9MC4xLCBjZXg9MC43LCB4LmludGVyc3A9MC41MCwgeS5pbnRlcnNwPTAuNDAsIHRleHQud2lkdGg9Miwgc2VnLmxlbj0xLCB0ZXh0LmZvbnQ9NCwgYm94Lmx0eT0wLCAKICAgICAgIGJ0eT0ibiIpCmBgYAojIyMjIyBIb2x0LVdpbnRlcnMgZm9yZWNhc3QKCipIb2x0IFdpbnRlcnMgbGluZWFyIHRyZW5kIG1ldGhvZCogw6ggdW5hIHRlY25pY2EgZGkgcHJldmlzaW9uZSBkZWxsZSBzZXJpZSB0ZW1wb3JhbGkgY2hlIHV0aWxpenphIHVuIG1vZGVsbG8gbGluZWFyZSBwZXIgbGEgY29tcG9uZW50ZSBkZWwgKnRyZW5kKi4KUGVyIGlsICp0cmVuZCosIGlsIG1ldG9kbyBkaSAqSG9sdC1XaW50ZXJzKiAgdXRpbGl6emEgdW4gbW9kZWxsbyBkaSByZWdyZXNzaW9uZSBsaW5lYXJlIHBlciBzdGltYXJlIGlsIHRyZW5kIGZ1dHVyby4gSW4gcGFydGljb2xhcmUsIGwnZXF1YXppb25lIGRlbCB0cmVuZCBsaW5lYXJlIMOoIGVzcHJlc3NhIGNvbWU6CgokVCh0KzEpID0gVCh0KSArIGIkCgpkb3ZlICRUKHQrMSkkIHJhcHByZXNlbnRhIGxhIHByZXZpc2lvbmUgZGVsIHRyZW5kIHBlciBpbCBwZXJpb2RvIHN1Y2Nlc3Npdm8sICRUKHQpJCByYXBwcmVzZW50YSBpbCB0cmVuZCBhdHR1YWxlIGUgYiByYXBwcmVzZW50YSBsYSBwZW5kZW56YSBkZWxsYSBsaW5lYSBkaSB0cmVuZC4KYGBge3J9CnkgPC0geV9CQ1RfbG9nX1NUTF9kZWZfd2luX2RjbXBfdHMkdHJlbmQKeV90cmVuZF9IV19wcmVkIDwtIGhvbHQoeSwgaD1Uc3RTX2xlbmd0aCkKeV90cmVuZF9IV19wcmVkX21lYW4gPC0gYXMubnVtZXJpYyh5X3RyZW5kX0hXX3ByZWQkbWVhbikKeV90cmVuZF9IV19mb3IgPC0gYyh5LHlfdHJlbmRfSFdfcHJlZF9tZWFuKQp4IDwtIGFzLkRhdGUoZGYkZGF0ZSkKeSA8LSB5X3RyZW5kX0hXX2ZvcgpwbG90KHhbMTpUcm5TX2xlbmd0aF0seVsxOlRyblNfbGVuZ3RoXSwgdHlwZT0ibCIsIGNvbD0iYmxhY2siLCB4YXh0ID0gIm4iLCAKICAgICB4bGltPWMoYXMuRGF0ZShkZiRkYXRlKVsxXSxhcy5EYXRlKGRmJGRhdGUpW1RTX2xlbmd0aF0pLAogICAgIHhsYWI9IiIsIHlsYWI9IlRyZW5kIiwgCiAgICBtYWluPSJIb2x0IFdpbnRlcnMgRm9yZWNhc3Qgb2YgdGhlIFRyZW5kIENvbXBvbmVudCBvZiB0aGUgU1RMIERlY29tcG9zaXRpb24gb2YgdGhlIExvZyBCb3gtQ294IFRyYW5zZm9ybWF0aW9uIiwKICAgIGNleC5tYWluPTAuOTUpCmxpbmVzKHhbVHJuU19sZW5ndGg6VFNfbGVuZ3RoXSwgeVtUcm5TX2xlbmd0aDpUU19sZW5ndGhdLCB0eXBlPSJsIiwgbHR5ID0gMSwgbHdkPTEuNSwgY29sPSJibHVlIikKYXhpcygxLCBhdD14W2Moc2VxKDEsVFNfbGVuZ3RoLGJ5PTYpLFRTX2xlbmd0aCldLCBsYWJlbHM9Zm9ybWF0KHhbYyhzZXEoMSxUU19sZW5ndGgsYnk9NiksVFNfbGVuZ3RoKV0sICIlWSAlbSIpLAogICAgIHRpY2s9VFJVRSwgY2V4LmF4aXMgPSAuNSkKbGVnZW5kKCJ0b3ByaWdodCIsIGluc2V0PWMoMC4wNiwwLjApLCBsZWdlbmQ9YygiVHJlbmQgQ29tcG9uZW50IiwgIkhvbHQgV2ludGVyIEZvcmVjYXN0IiksIGNvbD1jKCJibGFjayIsICJibHVlIiksIGx0eT0xLCAKICAgICAgIGx3ZD0wLjEsIGNleD0wLjcsIHguaW50ZXJzcD0wLjUwLCB5LmludGVyc3A9MC40MCwgdGV4dC53aWR0aD0yLCBzZWcubGVuPTEsIHRleHQuZm9udD00LCBib3gubHR5PTAsIGJ0eT0ibiIpCmBgYAojIyMjIyBHcmFuIE1lYW4gZm9yZWNhc3QKCkxhIHRlcnphIHRlY25pY2EgY29uc2lzdGUgbmVsIGNvbnNpZGVyYXJlIGxhIGdyYW4gbWVkaWEgZGVsbGEgY29tcG9uZW50ZSBkaSAqdHJlbmQqLCBjaGUgw6ggbGEgbWVkaWEgZGVsbGEgY29tcG9uZW50ZSBkaSB0cmVuZCBkZWwgKnRyYWluIHNldCogY29udGludWF0YSBpbiBtYW5pZXJhIGNvc3RhbnRlIHBlciB0dXR0YSBsYSBsdW5naGV6emEgZGVsICpUZXN0IHNldCouCmBgYHtyfQp5IDwtIHlfQkNUX2xvZ19TVExfZGVmX3dpbl9kY21wX3RzJHRyZW5kCnlfZ3Jhbl9tZWFuX3ByZWQgPC0gcmVwKG1lYW4oeSksVHN0U19sZW5ndGgpCnlfZ3Jhbl9tZWFuX2ZvciA8LSBjKHksIHJlcChtZWFuKHkpLFRzdFNfbGVuZ3RoKSkKdGFpbCh5X2dyYW5fbWVhbl9mb3IsMzApCnggPC0gYXMuRGF0ZShkZiRkYXRlKQp5IDwtIHlfZ3Jhbl9tZWFuX2ZvcgpwbG90KHhbMTpUcm5TX2xlbmd0aF0seVsxOlRyblNfbGVuZ3RoXSwgdHlwZT0ibCIsIGNvbD0iYmxhY2siLCB4YXh0ID0gIm4iLCAKICAgICB4bGltPWMoYXMuRGF0ZShkZiRkYXRlKVsxXSxhcy5EYXRlKGRmJGRhdGUpW1RTX2xlbmd0aF0pLAogICAgIHhsYWI9IiIsIHlsYWI9IlRyZW5kIiwgCiAgICBtYWluPSJIb2x0IFdpbnRlciBGb3JlY2FzdCBvZiB0aGUgVHJlbmQgQ29tcG9uZW50IG9mIHRoZSBTVEwgRGVjb21wb3NpdGlvbiBvZiB0aGUgTG9nIEJveC1Db3ggVHJhbnNmb3JtYXRpb24iLAogICAgY2V4Lm1haW49MC45NSkKbGluZXMoeFtUcm5TX2xlbmd0aDpUU19sZW5ndGhdLCB5W1RyblNfbGVuZ3RoOlRTX2xlbmd0aF0sIHR5cGU9ImwiLCBsdHkgPSAxLCBsd2Q9MS41LCBjb2w9ImJsdWUiKQpheGlzKDEsIGF0PXhbYyhzZXEoMSxUU19sZW5ndGgsYnk9NiksVFNfbGVuZ3RoKV0sIGxhYmVscz1mb3JtYXQoeFtjKHNlcSgxLFRTX2xlbmd0aCxieT02KSxUU19sZW5ndGgpXSwgIiVZICVtIiksCiAgICAgdGljaz1UUlVFLCBjZXguYXhpcyA9IC41KQpsZWdlbmQoInRvcHJpZ2h0IiwgaW5zZXQ9YygwLjA2LDAuMCksIGxlZ2VuZD1jKCJUcmVuZCBDb21wb25lbnQiLCAiR3JhbiBNZWFuIEZvcmVjYXN0IiksIGNvbD1jKCJibGFjayIsICJibHVlIiksIGx0eT0xLCAKICAgICAgIGx3ZD0wLjEsIGNleD0wLjcsIHguaW50ZXJzcD0wLjUwLCB5LmludGVyc3A9MC40MCwgdGV4dC53aWR0aD0yLCBzZWcubGVuPTEsIHRleHQuZm9udD00LCBib3gubHR5PTAsIGJ0eT0ibiIpCmBgYAojIyMjIFNlYXNvbmFsaXR5IGZvcmVjYXN0CgpQZXIgc2ltdWxhcmUgbGEgY29tcG9uZW50ZSBzdGFnaW9uYWxlIHBlciBpIHByb3NzaW1pIDEyIG1lc2kgw6ggYmFzdGF0byBwcm9sdW5nYXJlIGxhIGNvbXBvbmVudGUgc3RhZ2lvbmFsZSByaXBldGVuZG8gcGVyIGkgbWVzaSBmdXR1cmkgaSB2YWxvcmkgYXNzdW50aSBuZWkgbWVzaSBwYXNzYXRpIGNvcnJpc3BvbmRlbnRpICgqbmFpdmUgbWV0aG9kKikuCmBgYHtyfQp5X3NlYXMgPC0geV9CQ1RfbG9nX1NUTF9kZWZfd2luX2RjbXBfdHMkYHNlYXNvbl8xIHllYXJgCnlfc2Vhc19uYWl2ZV9wcmVkIDwtIGMoeV9zZWFzWyhsZW5ndGgoeV9zZWFzKS0xMSk6bGVuZ3RoKHlfc2VhcyldKQp5X3NlYXNfbmFpdmVfZm9yIDwtIGMoeV9zZWFzLCB5X3NlYXNfbmFpdmVfcHJlZCkKeCA8LSBhcy5EYXRlKGRmJGRhdGUpCnkgPC0geV9zZWFzX25haXZlX2ZvcgpwbG90KHhbMTpUcm5TX2xlbmd0aF0seVsxOlRyblNfbGVuZ3RoXSwgdHlwZT0ibCIsIGNvbD0iYmxhY2siLCB4YXh0ID0gIm4iLCB4bGFiPSIiLCAKICAgICB4bGltPWMoYXMuRGF0ZShkZiRkYXRlKVsxXSxhcy5EYXRlKGRmJGRhdGUpW1RTX2xlbmd0aF0pLAogICAgIHlsYWI9IlNlYXNvbmFsIiwgCiAgICAgbWFpbj0iU2Vhc29uYWwgTmFpdmUgRm9yZWNhc3Qgb2YgdGhlIFNlYXNvbmFsIENvbXBvbmVudCBvZiB0aGUgU1RMIERlY29tcG9zaXRpb24gb2YgdGhlIExvZyBCb3gtQ294IFRyYW5zZm9ybWF0aW9uIiwKICAgICBjZXgubWFpbj0wLjk1KQpsaW5lcyh4W1RyblNfbGVuZ3RoOlRTX2xlbmd0aF0sIHlbVHJuU19sZW5ndGg6VFNfbGVuZ3RoXSwgdHlwZT0ibCIsIGx0eSA9IDEsIGx3ZD0xLjUsIGNvbD0iYmx1ZSIpCmF4aXMoMSwgYXQ9eFtjKHNlcSgxLFRTX2xlbmd0aCxieT02KSxUU19sZW5ndGgpXSwgbGFiZWxzPWZvcm1hdCh4W2Moc2VxKDEsVFNfbGVuZ3RoLGJ5PTYpLFRTX2xlbmd0aCldLCAiJVkgJW0iKSwKICAgICB0aWNrPVRSVUUsIGNleC5heGlzID0gLjUpCmxlZ2VuZCgidG9wcmlnaHQiLCBpbnNldD1jKDAuMTIsMC4wKSwgbGVnZW5kPWMoIlNlYXNvbmFsIENvbXBvbmVudCIsICJTZWFzb25hbCBOYWl2ZSBGb3JlY2FzdCIpLCBjb2w9YygiYmxhY2siLCAiYmx1ZSIpLCBsdHk9MSwgCiAgICAgICBsd2Q9MC4xLCBjZXg9MC43LCB4LmludGVyc3A9MC41MCwgeS5pbnRlcnNwPTAuNDAsIHRleHQud2lkdGg9Miwgc2VnLmxlbj0xLCB0ZXh0LmZvbnQ9NCwgYm94Lmx0eT0wLCBidHk9Im4iKQpgYGAKCiMjIyMgVG90YWwgZm9yZWNhc3Qgd2l0aCAqbmFpdmUgbWV0aG9kKgpVbmEgdm9sdGEgYXBwbGljYXRpIGkgdmFyaSBtZXRvZGkgZGkgcHJlZGl6aW9uZSwgc2kgcHJvY2VkZSB1bmVuZG8gaW4gbWFuaWVyYSBhZGRpdGl2YSBpIHZhbG9yaSB0cm92YXRpIGUgc2kgdXRpbGl6emVyYW5ubyBtZXRyaWNoZSBkaSBhY2N1cmF0ZXp6YSBwZXIgdmVyaWZpY2FyZSBxdWFsaSB0aXBpIGRpIHByZWRpemlvbmkgcmVzdGl0dWlzY29ubyByaXN1bHRhdGkgbWlnbGlvcmkuCgpJbCBwcmltbyAqZm9yZWNhc3QqIMOoIG90dGVudXRvIHVuZW5kbyAiKm5haXZlIHRyZW5kIG1ldGhvZCoiICsgIipuYWl2ZSBzZWFzb25hbGl0eSBtZXRob2QqIiArICIqYXJpbWEoNCwwLDYpIHJlbWFpbmRlcioiCmBgYHtyfQojaGVhZChkZikKeV9yZW1fcHJlZF9mb3JfbWVhbiA8LSBhcy52ZWN0b3IoeV9yZW1fcHJlZF9mb3JfbWVhbikKc2FsZXNfbG9nX25haXZlX3BvaW50X3ByZWQgPC0geV90cmVuZF9uYWl2ZV9wcmVkICsgeV9zZWFzX25haXZlX3ByZWQgKyB5X3JlbV9wcmVkX2Zvcl9tZWFuCnNhbGVzX2xvZ19uYWl2ZV9wb2ludF9mb3IgPC0gYyhzYWxlc19sb2dbYygxOlRyblNfbGVuZ3RoKV0sIHNhbGVzX2xvZ19uYWl2ZV9wb2ludF9wcmVkKQp5X2xvZ19uYWl2ZV9wcmVkX1JIX2Zvcl8wODBfbG93X2ludCA8LSB5X3RyZW5kX25haXZlX3ByZWQgKyB5X3NlYXNfbmFpdmVfcHJlZCArIHlfcmVzX2xvZ19wcmVkX1JIX2Zvcl8wODBfbG93X2ludAp5X2xvZ19uYWl2ZV9wcmVkX1JIX2Zvcl8wODBfdXBwX2ludCA8LSB5X3RyZW5kX25haXZlX3ByZWQgKyB5X3NlYXNfbmFpdmVfcHJlZCArIHlfcmVzX2xvZ19wcmVkX1JIX2Zvcl8wODBfdXBwX2ludAp5X2xvZ19uYWl2ZV9wcmVkX1JIX2Zvcl8wOTVfbG93X2ludCA8LSB5X3RyZW5kX25haXZlX3ByZWQgKyB5X3NlYXNfbmFpdmVfcHJlZCArIHlfcmVzX2xvZ19wcmVkX1JIX2Zvcl8wOTVfbG93X2ludAp5X2xvZ19uYWl2ZV9wcmVkX1JIX2Zvcl8wOTVfdXBwX2ludCA8LSB5X3RyZW5kX25haXZlX3ByZWQgKyB5X3NlYXNfbmFpdmVfcHJlZCArIHlfcmVzX2xvZ19wcmVkX1JIX2Zvcl8wOTVfdXBwX2ludAoKc2FsZXNfbG9nX2Jvb3RfMDgwX2xvd19uYWl2ZV9mb3JfaW50IDwtIGMocmVwKE5BLFRyblNfbGVuZ3RoKSx5X2xvZ19uYWl2ZV9wcmVkX1JIX2Zvcl8wODBfbG93X2ludCkKc2FsZXNfbG9nX2Jvb3RfMDgwX3VwcF9uYWl2ZV9mb3JfaW50IDwtIGMocmVwKE5BLFRyblNfbGVuZ3RoKSx5X2xvZ19uYWl2ZV9wcmVkX1JIX2Zvcl8wODBfdXBwX2ludCkKc2FsZXNfbG9nX2Jvb3RfMDk1X2xvd19uYWl2ZV9mb3JfaW50IDwtIGMocmVwKE5BLFRyblNfbGVuZ3RoKSx5X2xvZ19uYWl2ZV9wcmVkX1JIX2Zvcl8wOTVfbG93X2ludCkKc2FsZXNfbG9nX2Jvb3RfMDk1X3VwcF9uYWl2ZV9mb3JfaW50IDwtIGMocmVwKE5BLFRyblNfbGVuZ3RoKSx5X2xvZ19uYWl2ZV9wcmVkX1JIX2Zvcl8wOTVfdXBwX2ludCkKCnNhbGVzX25haXZlX3ByZWRfZGYgPC0gYWRkX2NvbHVtbihkZiwgc2FsZXNfbG9nPXNhbGVzX2xvZywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsZXNfbG9nX25haXZlX3BvaW50X2Zvcj1zYWxlc19sb2dfbmFpdmVfcG9pbnRfZm9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGVzX2xvZ19ib290XzA4MF9sb3dfbmFpdmVfZm9yX2ludD1zYWxlc19sb2dfYm9vdF8wODBfbG93X25haXZlX2Zvcl9pbnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGVzX2xvZ19ib290XzA4MF91cHBfbmFpdmVfZm9yX2ludD1zYWxlc19sb2dfYm9vdF8wODBfdXBwX25haXZlX2Zvcl9pbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsZXNfbG9nX2Jvb3RfMDk1X2xvd19uYWl2ZV9mb3JfaW50PXNhbGVzX2xvZ19ib290XzA5NV9sb3dfbmFpdmVfZm9yX2ludCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsZXNfbG9nX2Jvb3RfMDk1X3VwcF9uYWl2ZV9mb3JfaW50PXNhbGVzX2xvZ19ib290XzA5NV91cHBfbmFpdmVfZm9yX2ludCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuYWZ0ZXI9InNhbGVzIikKdGFpbChzYWxlc19uYWl2ZV9wcmVkX2RmLCAyMCkKYGBgClZpZW5lIGluaXppYWxtZW50ZSBlZmZldHR1YXRvIHVuIHBsb3QgaW4gY3VpIGluIGN1aSB2aWVuZSBtZXNzbyBhIGNvbmZyb250byBpbCAqcGF0aCogcmVhbGUgZGVpIGRhdGkgY29uIHF1ZWxsbyBwcmVkZXR0byB1dGlsaXp6YW5kbyBzb2xvIGxlIG1lZGllIGRlbGxlIHByZWRpemlvbmkuCmBgYHtyfQphdXRvcGxvdCh0cyhsb2coZGZfQ2FtcnlfVVNfc2FsZXMkc2FsZXMpLCBzdGFydD1jKDIwMDksIDAxKSwgZW5kPWMoMjAyMiwgMTEpLCBmcmVxdWVuY3kgPSAxMiksIHNlcmllID0iVG95b3RhIENhbXJ5IHBhdGgiKSArCiAgYXV0b2xheWVyKHRzKHNhbGVzX2xvZ19uYWl2ZV9wb2ludF9wcmVkLCBzdGFydD1jKDIwMjEsIDEyKSwgZW5kPWMoMjAyMiwgMTEpLCBmcmVxdWVuY3kgPSAxMiksIHNlcmllcz0iRm9yZWNhc3Qgd2l0aCBuYWl2ZSB0cmVuZCIpICsKICB4bGFiKCJNb250aCIpICsgeWxhYigiU2FsZXMiKSArCiAgZ2d0aXRsZSgiRm9yZWNhc3QgVG95b3RhIENhbXJ5IHNhbGVzIikgKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJMZWdlbmQiKSkKYGBgClBlciBwb2kgcGFzc2FyZSBhIHVuIHBsb3QgcGnDuSBjb21wbGV0byBpbiBjdWkgc29ubyBwcmVzZW50aSBnbGkgaW50ZXJ2YWxsaSBkaSBwcmVkaXppb25lIGRhdGkgZGFsbG8gc3R1ZGlvIGRlaSByZXNpZHVpIGRlbCBtb2RlbGxvICpBUk1BKi4KYGBge3J9CkRhdGFfZGYgPC0gc2FsZXNfbmFpdmVfcHJlZF9kZgpsZW5ndGggPC0gbnJvdyhEYXRhX2RmKQpUIDwtIFRyblNfbGVuZ3RoCnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKGF0b3AoIkxpbmUgUGxvdCBvZiB0aGUgVG95b3RhIENhbXJ5IHNhbGVzIFRyYWluaW5nIFNldCBhbmQgUHJlZGljdGVkIFRlc3QgU2V0cyAtIGZyb20gIiwgLihGaXJzdF9EYXRlKSwgIiB0byAiLCAuKExhc3RfRGF0ZSkpKQpzdWJ0aXRsZV9jb250ZW50IDwtIGJxdW90ZShwYXN0ZSgiVHJhaW5pbmcgc2V0IGxlbmd0aCAiLCAuKFRyblNfbGVuZ3RoKSwgIiBzYW1wbGUgcG9pbnRzLiBUZXN0IHNldCBsZW5ndGggIiwgLihUc3RTX2xlbmd0aCksICIgc2FtcGxlIHBvaW50cy4iKSkKY2FwdGlvbl9jb250ZW50IDwtICgiQXV0aG9yOiBNYXR0ZW8gQ2hpYWNjaGlhIikKeF9uYW1lIDwtIGJxdW90ZSgiIikKIyBsaWJyYXJ5KG51bWJlcnMpCiMgcHJpbWVGYWN0b3JzKFQpCnhfYnJlYWtzX251bSA8LSAzMwp4X2JyZWFrc19taW4gPC0gRGF0YV9kZiR0WzFdCnhfYnJlYWtzX21heCA8LSBEYXRhX2RmJHRbbGVuZ3RoXQp4X2JpbndpZHRoIDwtIGZsb29yKCh4X2JyZWFrc19tYXgteF9icmVha3NfbWluKS94X2JyZWFrc19udW0pCnhfYnJlYWtzIDwtIHNlcShmcm9tPXhfYnJlYWtzX21pbiwgdG89eF9icmVha3NfbWF4LCBieT14X2JpbndpZHRoKQppZigoeF9icmVha3NfbWF4LW1heCh4X2JyZWFrcykpPnhfYmlud2lkdGgvMil7eF9icmVha3MgPC0gYyh4X2JyZWFrcyx4X2JyZWFrc19tYXgpfQp4X2xhYnMgPC0gcGFzdGUoRGF0YV9kZiRNb250aFt4X2JyZWFrc10sRGF0YV9kZiRZZWFyW3hfYnJlYWtzXSkKSiA8LSAwCnhfbGltcyA8LSBjKHhfYnJlYWtzX21pbi1KKnhfYmlud2lkdGgsIHhfYnJlYWtzX21heCtKKnhfYmlud2lkdGgpCnlfbmFtZSA8LSBicXVvdGUoIlNhbGVzIikKeV9icmVha3NfbnVtIDwtIDEwCnlfbWF4IDwtIG1heChuYS5vbWl0KERhdGFfZGYkc2FsZXNfbG9nKSxuYS5vbWl0KERhdGFfZGYkc2FsZXNfbG9nX2Jvb3RfMDk1X3VwcF9uYWl2ZV9mb3JfaW50KSkKeV9taW4gPC0gbWluKG5hLm9taXQoRGF0YV9kZiRzYWxlc19sb2cpLG5hLm9taXQoRGF0YV9kZiRzYWxlc19sb2dfYm9vdF8wOTVfbG93X25haXZlX2Zvcl9pbnQpKQp5X2JpbndpZHRoIDwtIHJvdW5kKCh5X21heC15X21pbikveV9icmVha3NfbnVtLCBkaWdpdHM9MykKeV9icmVha3NfbG93IDwtIGZsb29yKHlfbWluL3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3NfdXAgPC0gY2VpbGluZyh5X21heC95X2JpbndpZHRoKSp5X2JpbndpZHRoCnlfYnJlYWtzIDwtIHJvdW5kKHNlcShmcm9tPXlfYnJlYWtzX2xvdywgdG89eV9icmVha3NfdXAsIGJ5PXlfYmlud2lkdGgpLGRpZ2l0cz0zKQp5X2xhYnMgPC0gZm9ybWF0KHlfYnJlYWtzLCBzY2llbnRpZmljPUZBTFNFKQpLIDwtIDAKeV9saW1zIDwtIGMoKHlfYnJlYWtzX2xvdy1LKnlfYmlud2lkdGgpLCAoeV9icmVha3NfdXArSyp5X2JpbndpZHRoKSkKbGluZV9ibGFjayAgIDwtIGJxdW90ZSgiaW4tc2FtcGxlIHBhdGgiKQpsaW5lX21hZ2VudGEgPC0gYnF1b3RlKCJyZWFsIHBhdGgiKQpsaW5lX2Jyb3duICAgPC0gYnF1b3RlKCJwcmVkaWN0ZWQgcGF0aCIpCmxpbmVfZ3JlZW4gICA8LSBicXVvdGUoIjgwJSBwcmVkLmludC4iKQojIGxpbmVfYmx1ZSAgICA8LSBicXVvdGUoIjk1JSBwcmVkLmludC4iKQpsaW5lX3JlZCAgICAgPC0gYnF1b3RlKCI5NSUgcHJlZC5pbnQuIikKbGVnX2xpbmVfbGFicyAgIDwtIGMobGluZV9ibGFjaywgbGluZV9icm93biwgbGluZV9tYWdlbnRhLCBsaW5lX2dyZWVuLCBsaW5lX3JlZCkKbGVnX2xpbmVfYnJlYWtzIDwtIGMoImxpbmVfYmxhY2siLCAibGluZV9icm93biIsICJsaW5lX21hZ2VudGEiLCAibGluZV9ncmVlbiIsICJsaW5lX3JlZCIpCmxlZ19saW5lX2NvbHMgICA8LSBjKCJsaW5lX2JsYWNrIj0iYmxhY2siLCAibGluZV9icm93biI9ImJyb3duIiwgImxpbmVfbWFnZW50YSI9Im1hZ2VudGEiLAogICAgICAgICAgICAgICAgICAgICAibGluZV9ncmVlbiI9ImdyZWVuIiwgImxpbmVfcmVkIj0icmVkIikKIyBsZWdfbGluZV9sYWJzICAgPC0gYyhsaW5lX2JsYWNrLCBsaW5lX2Jyb3duLCBsaW5lX21hZ2VudGEsIGxpbmVfZ3JlZW4sIGxpbmVfYmx1ZSwgbGluZV9yZWQpCiMgbGVnX2xpbmVfYnJlYWtzIDwtIGMoImxpbmVfYmxhY2siLCAibGluZV9icm93biIsICJsaW5lX21hZ2VudGEiLCAibGluZV9ncmVlbiIsICJsaW5lX2JsdWUiLCAibGluZV9yZWQiKQojIGxlZ19saW5lX2NvbHMgICA8LSBjKCJsaW5lX2JsYWNrIj0iYmxhY2siLCAibGluZV9icm93biI9ImJyb3duIiwgImxpbmVfbWFnZW50YSI9Im1hZ2VudGEiLAojICAgICAgICAgICAgICAgICAgICAgICJsaW5lX2dyZWVuIj0iZ3JlZW4iLCAibGluZV9ibHVlIj0iYmx1ZSIsICJsaW5lX3JlZCI9InJlZCIpCmZpbGxfZyA8LSBicXVvdGUoIjkwJSBwcmVkLiBiYW5kIikKIyBmaWxsX2IgPC0gYnF1b3RlKCI5NSUgcHJlZC4gYmFuZCIpCmZpbGxfciA8LSBicXVvdGUoIjk1JSBwcmVkLiBiYW5kIikKZmlsbF9nIDwtIGJxdW90ZSgiOTAlIHByZWQuIGJhbmQiKQpmaWxsX2IgPC0gYnF1b3RlKCI5NSUgcHJlZC4gYmFuZCIpCmZpbGxfciA8LSBicXVvdGUoIjk5JSBwcmVkLiBiYW5kIikKbGVnX2ZpbGxfbGFicyAgIDwtIGMoIGZpbGxfZywgZmlsbF9yKQpsZWdfZmlsbF9icmVha3MgPC0gYygiZmlsbF9nIiwgImZpbGxfciIpCmxlZ19maWxsX2NvbHMgICA8LSBjKCJmaWxsX2ciPSJsaWdodGdyZWVuIiwgImZpbGxfciI9Im9yYW5nZXJlZCIpCmxlZ19maWxsX2xhYnMgICA8LSBjKCBmaWxsX2csIGZpbGxfYiwgZmlsbF9yKQpsZWdfZmlsbF9icmVha3MgPC0gYygiZmlsbF9nIiwgImZpbGxfYiIsICJmaWxsX3IiKQpsZWdfZmlsbF9jb2xzICAgPC0gYygiZmlsbF9nIj0ibGlnaHRncmVlbiIsICJmaWxsX2IiPSJibHVlIiwgImZpbGxfciI9Im9yYW5nZXJlZCIpCmxlZ19jb2xfbGFicyAgICA8LSBsZWdfbGluZV9sYWJzCmxlZ19jb2xfYnJlYWtzICA8LSBsZWdfbGluZV9icmVha3MKbGVnX2NvbF9jb2xzICAgIDwtIGxlZ19saW5lX2NvbHMKeV9wcmVkX2xwIDwtIGdncGxvdChEYXRhX2RmLCBhZXMoeD10KSkgKyAKICBnZW9tX2xpbmUoZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0IDw9IHRbVCsxXSksIGFlcyh5PXNhbGVzX2xvZywgY29sb3I9ImxpbmVfYmxhY2siKSwKICAgICAgICAgICAgbGluZXR5cGU9InNvbGlkIiwgYWxwaGE9MSwgc2l6ZT0wLjMsIGdyb3VwPTEpICsKICBnZW9tX2xpbmUoZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFlcyh5PXNhbGVzX2xvZywgY29sb3I9ImxpbmVfbWFnZW50YSIpLAogICAgICAgICAgICBsaW5ldHlwZT0ic29saWQiLCBhbHBoYT0xLCBzaXplPTAuMywgZ3JvdXA9MSkgKwogIGdlb21fbGluZShkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPj0gdFtUKzFdKSwgYWVzKHk9c2FsZXNfbG9nX25haXZlX3BvaW50X2ZvciAsIGNvbG91cj0ibGluZV9icm93biIpLAogICAgICAgICAgICBsaW5ldHlwZT0ic29saWQiLCBhbHBoYT0xLCBzaXplPTAuMykgKwogIGdlb21fbGluZShkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPj0gdFtUKzFdKSwgYWVzKHk9c2FsZXNfbG9nX2Jvb3RfMDk1X2xvd19uYWl2ZV9mb3JfaW50LCBjb2xvdXI9ImxpbmVfcmVkIiksCiAgICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsIGFscGhhPTEsIHNpemU9MC4zKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhZXMoeT1zYWxlc19sb2dfYm9vdF8wOTVfdXBwX25haXZlX2Zvcl9pbnQsIGNvbG91cj0ibGluZV9yZWQiKSwKICAgICAgICAgICAgbGluZXR5cGU9InNvbGlkIiwgYWxwaGE9MSwgc2l6ZT0wLjMpICsKICBnZW9tX2xpbmUoZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFlcyh5PXNhbGVzX2xvZ19ib290XzA4MF9sb3dfbmFpdmVfZm9yX2ludCwgY29sb3VyPSJsaW5lX2dyZWVuIiksCiAgICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsIGFscGhhPTEsIHNpemU9MC4zKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhZXMoeT1zYWxlc19sb2dfYm9vdF8wODBfdXBwX25haXZlX2Zvcl9pbnQsIGNvbG91cj0ibGluZV9ncmVlbiIpLAogICAgICAgICAgICBsaW5ldHlwZT0ic29saWQiLCBhbHBoYT0xLCBzaXplPTAuMykgKwogIGdlb21fcmliYm9uKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhbHBoYT0wLjMsIGNvbG91cj0ib3JhbmdlcmVkIiwKICAgICAgICAgICAgICBhZXMoeW1pbj1zYWxlc19sb2dfYm9vdF8wOTVfbG93X25haXZlX2Zvcl9pbnQsIHltYXg9c2FsZXNfbG9nX2Jvb3RfMDk1X3VwcF9uYWl2ZV9mb3JfaW50LCBmaWxsPSJmaWxsX3IiKSkgKwogIGdlb21fcmliYm9uKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhbHBoYT0wLjMsIGNvbG91cj0ib3JhbmdlcmVkIiwKICAgICAgICAgICAgICBhZXMoeW1pbj1zYWxlc19sb2dfYm9vdF8wOTVfbG93X25haXZlX2Zvcl9pbnQsIHltYXg9c2FsZXNfbG9nX2Jvb3RfMDk1X3VwcF9uYWl2ZV9mb3JfaW50LCBmaWxsPSJmaWxsX3IiKSkgKwogIGdlb21fcmliYm9uKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhbHBoYT0wLjMsIGNvbG91cj0iYmx1ZSIsCiAgICAgICAgICAgICAgYWVzKHltaW49c2FsZXNfbG9nX2Jvb3RfMDk1X2xvd19uYWl2ZV9mb3JfaW50LCB5bWF4PXNhbGVzX2xvZ19ib290XzA4MF9sb3dfbmFpdmVfZm9yX2ludCwgZmlsbD0iZmlsbF9iIikpICsKICBnZW9tX3JpYmJvbihkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPj0gdFtUKzFdKSwgYWxwaGE9MC4zLCBjb2xvdXI9ImJsdWUiLAogICAgICAgICAgICAgIGFlcyh5bWluPXNhbGVzX2xvZ19ib290XzA4MF91cHBfbmFpdmVfZm9yX2ludCwgeW1heD1zYWxlc19sb2dfYm9vdF8wOTVfdXBwX25haXZlX2Zvcl9pbnQsIGZpbGw9ImZpbGxfYiIpKSArCiAgZ2VvbV9yaWJib24oZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFscGhhPTAuMywgY29sb3VyPSJncmVlbiIsCiAgICAgICAgICAgICAgYWVzKHltaW49c2FsZXNfbG9nX2Jvb3RfMDgwX2xvd19uYWl2ZV9mb3JfaW50LCB5bWF4PXNhbGVzX2xvZ19ib290XzA4MF91cHBfbmFpdmVfZm9yX2ludCwgZmlsbD0iZmlsbF9nIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZT14X25hbWUsIGJyZWFrcz14X2JyZWFrcywgbGFiZWxzPXhfbGFicywgbGltaXRzPXhfbGltcykgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lPXlfbmFtZSwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9TlVMTCwgbGltaXRzPXlfbGltcywKICAgICAgICAgICAgICAgICAgICAgc2VjLmF4aXM9IHNlY19heGlzKH4uLCBicmVha3M9eV9icmVha3MsIGxhYmVscz15X2xhYnMpKSArCiAgZ2d0aXRsZSh0aXRsZV9jb250ZW50KSArCiAgbGFicyhzdWJ0aXRsZT1zdWJ0aXRsZV9jb250ZW50LCBjYXB0aW9uPWNhcHRpb25fY29udGVudCkgKwogIGd1aWRlcyhsaW5ldHlwZT0ibm9uZSIsIHNoYXBlPSJub25lIikgKwogIHNjYWxlX2NvbG91cl9tYW51YWwobmFtZT0iTGVnZW5kIiwgbGFiZWxzPWxlZ19saW5lX2xhYnMsIHZhbHVlcz1sZWdfbGluZV9jb2xzLCBicmVha3M9bGVnX2xpbmVfYnJlYWtzKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iIiwgbGFiZWxzPWxlZ19maWxsX2xhYnMsIHZhbHVlcz1sZWdfZmlsbF9jb2xzLCBicmVha3M9bGVnX2ZpbGxfYnJlYWtzKSArCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQob3JkZXI9MSksIGZpbGw9Z3VpZGVfbGVnZW5kKG9yZGVyPTIpKSArCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCAKICAgICAgICBwbG90LnN1YnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdCA9ICAwLjUpLAogICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEuMCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9LTQ1LCB2anVzdD0xLCBoanVzdD0tMC4zKSwKICAgICAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjgsImNtIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKcGxvdCh5X3ByZWRfbHApCmBgYAojIyMjIFRvdGFsIGZvcmVjYXN0IHdpdGggKkhvbHQgV2ludGVycyBtZXRob2QqCgpJbCBzZWNvbmRvICpmb3JlY2FzdCogw6ggb3R0ZW51dG8gdW5lbmRvICIqSG9sdCBXaW50ZXJzIHRyZW5kIG1ldGhvZCoiICsgIipuYWl2ZSBzZWFzb25hbGl0eSBtZXRob2QqIiArICIqYXJpbWEoNCwwLDYpIHJlbWFpbmRlcioiCmBgYHtyfQojaGVhZChkZikKc2FsZXNfbG9nIDwtIGFzLnZlY3Rvcihsb2coZGYkc2FsZXMpKQpzYWxlc19sb2dfdGVzdCA8LSBzYWxlc19sb2dbYygoVHJuU19sZW5ndGgrMSk6KFRyblNfbGVuZ3RoK1RzdFNfbGVuZ3RoKSldCnlfcmVtX3ByZWRfZm9yX21lYW4gPC0gYXMudmVjdG9yKHlfcmVtX3ByZWRfZm9yX21lYW4pCnNhbGVzX2xvZ19IV19wb2ludF9wcmVkIDwtIHlfdHJlbmRfSFdfcHJlZF9tZWFuICsgeV9zZWFzX25haXZlX3ByZWQgKyB5X3JlbV9wcmVkX2Zvcl9tZWFuCiNzaG93KHNhbGVzX2xvZ19uYWl2ZV9wb2ludF9wcmVkKQpzYWxlc19sb2dfSFdfcG9pbnRfZm9yIDwtIGMoc2FsZXNfbG9nW2MoMTpUcm5TX2xlbmd0aCldLCBzYWxlc19sb2dfSFdfcG9pbnRfcHJlZCkKeV9sb2dfSFdfcHJlZF9SSF9mb3JfMDgwX2xvd19pbnQgPC0geV90cmVuZF9IV19wcmVkX21lYW4gKyB5X3NlYXNfbmFpdmVfcHJlZCAgKyB5X3Jlc19sb2dfcHJlZF9SSF9mb3JfMDgwX2xvd19pbnQKeV9sb2dfSFdfcHJlZF9SSF9mb3JfMDgwX3VwcF9pbnQgPC0geV90cmVuZF9IV19wcmVkX21lYW4gKyB5X3NlYXNfbmFpdmVfcHJlZCAgKyB5X3Jlc19sb2dfcHJlZF9SSF9mb3JfMDgwX3VwcF9pbnQKeV9sb2dfSFdfcHJlZF9SSF9mb3JfMDk1X2xvd19pbnQgPC0geV90cmVuZF9IV19wcmVkX21lYW4gKyB5X3NlYXNfbmFpdmVfcHJlZCAgKyB5X3Jlc19sb2dfcHJlZF9SSF9mb3JfMDk1X2xvd19pbnQKeV9sb2dfSFdfcHJlZF9SSF9mb3JfMDk1X3VwcF9pbnQgPC0geV90cmVuZF9IV19wcmVkX21lYW4gKyB5X3NlYXNfbmFpdmVfcHJlZCAgKyB5X3Jlc19sb2dfcHJlZF9SSF9mb3JfMDk1X3VwcF9pbnQKCnNhbGVzX2xvZ19ib290XzA4MF9sb3dfSFdfZm9yX2ludCA8LSBjKHJlcChOQSxUcm5TX2xlbmd0aCkseV9sb2dfSFdfcHJlZF9SSF9mb3JfMDgwX2xvd19pbnQpCnNhbGVzX2xvZ19ib290XzA4MF91cHBfSFdfZm9yX2ludCA8LSBjKHJlcChOQSxUcm5TX2xlbmd0aCkseV9sb2dfSFdfcHJlZF9SSF9mb3JfMDgwX3VwcF9pbnQpCnNhbGVzX2xvZ19ib290XzA5NV9sb3dfSFdfZm9yX2ludCA8LSBjKHJlcChOQSxUcm5TX2xlbmd0aCkseV9sb2dfSFdfcHJlZF9SSF9mb3JfMDk1X2xvd19pbnQpCnNhbGVzX2xvZ19ib290XzA5NV91cHBfSFdfZm9yX2ludCA8LSBjKHJlcChOQSxUcm5TX2xlbmd0aCkseV9sb2dfSFdfcHJlZF9SSF9mb3JfMDk1X3VwcF9pbnQpCgpzYWxlc19IV19wcmVkX2RmIDwtIGFkZF9jb2x1bW4oZGYsIHNhbGVzX2xvZz1zYWxlc19sb2csIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGVzX2xvZ19IV19wb2ludF9mb3I9c2FsZXNfbG9nX0hXX3BvaW50X2ZvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxlc19sb2dfYm9vdF8wODBfbG93X0hXX2Zvcl9pbnQ9c2FsZXNfbG9nX2Jvb3RfMDgwX2xvd19IV19mb3JfaW50LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxlc19sb2dfYm9vdF8wODBfdXBwX0hXX2Zvcl9pbnQ9c2FsZXNfbG9nX2Jvb3RfMDgwX3VwcF9IV19mb3JfaW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGVzX2xvZ19ib290XzA5NV9sb3dfSFdfZm9yX2ludD1zYWxlc19sb2dfYm9vdF8wOTVfbG93X0hXX2Zvcl9pbnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGVzX2xvZ19ib290XzA5NV91cHBfSFdfZm9yX2ludD1zYWxlc19sb2dfYm9vdF8wOTVfdXBwX0hXX2Zvcl9pbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmFmdGVyPSJzYWxlcyIpCmBgYAoKVmllbmUgaW5pemlhbG1lbnRlIGVmZmV0dHVhdG8gdW4gcGxvdCBpbiBjdWkgaW4gY3VpIHZpZW5lIG1lc3NvIGEgY29uZnJvbnRvIGlsICpwYXRoKiByZWFsZSBkZWkgZGF0aSBjb24gcXVlbGxvIHByZWRldHRvLgpgYGB7cn0KYXV0b3Bsb3QodHMobG9nKGRmX0NhbXJ5X1VTX3NhbGVzJHNhbGVzKSwgc3RhcnQ9YygyMDA5LCAwMSksIGVuZD1jKDIwMjIsIDExKSwgZnJlcXVlbmN5ID0gMTIpLCBzZXJpZXM9ICJUb3lvdGEgQ2FtcnkgcGF0aCIpICsKICBhdXRvbGF5ZXIodHMoc2FsZXNfbG9nX0hXX3BvaW50X3ByZWQsIHN0YXJ0PWMoMjAyMSwgMTIpLCBlbmQ9YygyMDIyLCAxMSksIGZyZXF1ZW5jeSA9IDEyKSwgc2VyaWVzPSJGb3JlY2FzdCB3aXRoIEhXIHRyZW5kIikgKwogIHhsYWIoIk1vbnRoIikgKyB5bGFiKCJTYWxlcyIpICsKICBnZ3RpdGxlKCJGb3JlY2FzdCBUb3lvdGEgQ2Ftcnkgc2FsZXMiKSArCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IkxlZ2VuZCIpKQpgYGAKUGVyIHBvaSBwYXNzYXJlIGEgdW4gcGxvdCBwacO5IGNvbXBsZXRvIGluIGN1aSBzb25vIHByZXNlbnRpIGdsaSBpbnRlcnZhbGxpIGRpIHByZWRpemlvbmUgZGF0aSBkYWxsbyBzdHVkaW8gZGVpIHJlc2lkdWkgZGVsIG1vZGVsbG8gKkFSTUEqLgpgYGB7cn0KRGF0YV9kZiA8LSBzYWxlc19IV19wcmVkX2RmCmxlbmd0aCA8LSBucm93KERhdGFfZGYpClQgPC0gVHJuU19sZW5ndGgKdGl0bGVfY29udGVudCA8LSBicXVvdGUoYXRvcCgiTGluZSBQbG90IG9mIHRoZSBUb3lvdGEgQ2Ftcnkgc2FsZXMgVHJhaW5pbmcgU2V0IGFuZCBQcmVkaWN0ZWQgVGVzdCBTZXRzIC0gZnJvbSAiLCAuKEZpcnN0X0RhdGUpLCAiIHRvICIsIC4oTGFzdF9EYXRlKSkpCnN1YnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKHBhc3RlKCJUcmFpbmluZyBzZXQgbGVuZ3RoICIsIC4oVHJuU19sZW5ndGgpLCAiIHNhbXBsZSBwb2ludHMuIFRlc3Qgc2V0IGxlbmd0aCAiLCAuKFRzdFNfbGVuZ3RoKSwgIiBzYW1wbGUgcG9pbnRzLiIpKQpjYXB0aW9uX2NvbnRlbnQgPC0gKCJBdXRob3I6IE1hdHRlbyBDaGlhY2NoaWEiKQp4X25hbWUgPC0gYnF1b3RlKCIiKQojIGxpYnJhcnkobnVtYmVycykKIyBwcmltZUZhY3RvcnMoVCkKeF9icmVha3NfbnVtIDwtIDMzCnhfYnJlYWtzX21pbiA8LSBEYXRhX2RmJHRbMV0KeF9icmVha3NfbWF4IDwtIERhdGFfZGYkdFtsZW5ndGhdCnhfYmlud2lkdGggPC0gZmxvb3IoKHhfYnJlYWtzX21heC14X2JyZWFrc19taW4pL3hfYnJlYWtzX251bSkKeF9icmVha3MgPC0gc2VxKGZyb209eF9icmVha3NfbWluLCB0bz14X2JyZWFrc19tYXgsIGJ5PXhfYmlud2lkdGgpCmlmKCh4X2JyZWFrc19tYXgtbWF4KHhfYnJlYWtzKSk+eF9iaW53aWR0aC8yKXt4X2JyZWFrcyA8LSBjKHhfYnJlYWtzLHhfYnJlYWtzX21heCl9CnhfbGFicyA8LSBwYXN0ZShEYXRhX2RmJE1vbnRoW3hfYnJlYWtzXSxEYXRhX2RmJFllYXJbeF9icmVha3NdKQpKIDwtIDAKeF9saW1zIDwtIGMoeF9icmVha3NfbWluLUoqeF9iaW53aWR0aCwgeF9icmVha3NfbWF4K0oqeF9iaW53aWR0aCkKeV9uYW1lIDwtIGJxdW90ZSgiU2FsZXMiKQp5X2JyZWFrc19udW0gPC0gMTAKeV9tYXggPC0gbWF4KG5hLm9taXQoRGF0YV9kZiRzYWxlc19sb2cpLG5hLm9taXQoRGF0YV9kZiRzYWxlc19sb2dfYm9vdF8wOTVfdXBwX0hXX2Zvcl9pbnQpKQp5X21pbiA8LSBtaW4obmEub21pdChEYXRhX2RmJHNhbGVzX2xvZyksbmEub21pdChEYXRhX2RmJHNhbGVzX2xvZ19ib290XzA5NV9sb3dfSFdfZm9yX2ludCkpCnlfYmlud2lkdGggPC0gcm91bmQoKHlfbWF4LXlfbWluKS95X2JyZWFrc19udW0sIGRpZ2l0cz0zKQp5X2JyZWFrc19sb3cgPC0gZmxvb3IoeV9taW4veV9iaW53aWR0aCkqeV9iaW53aWR0aAp5X2JyZWFrc191cCA8LSBjZWlsaW5nKHlfbWF4L3lfYmlud2lkdGgpKnlfYmlud2lkdGgKeV9icmVha3MgPC0gcm91bmQoc2VxKGZyb209eV9icmVha3NfbG93LCB0bz15X2JyZWFrc191cCwgYnk9eV9iaW53aWR0aCksZGlnaXRzPTMpCnlfbGFicyA8LSBmb3JtYXQoeV9icmVha3MsIHNjaWVudGlmaWM9RkFMU0UpCksgPC0gMAp5X2xpbXMgPC0gYygoeV9icmVha3NfbG93LUsqeV9iaW53aWR0aCksICh5X2JyZWFrc191cCtLKnlfYmlud2lkdGgpKQpsaW5lX2JsYWNrICAgPC0gYnF1b3RlKCJpbi1zYW1wbGUgcGF0aCIpCmxpbmVfbWFnZW50YSA8LSBicXVvdGUoInJlYWwgcGF0aCIpCmxpbmVfYnJvd24gICA8LSBicXVvdGUoInByZWRpY3RlZCBwYXRoIikKbGluZV9ncmVlbiAgIDwtIGJxdW90ZSgiODAlIHByZWQuaW50LiIpCiMgbGluZV9ibHVlICAgIDwtIGJxdW90ZSgiOTUlIHByZWQuaW50LiIpCmxpbmVfcmVkICAgICA8LSBicXVvdGUoIjk1JSBwcmVkLmludC4iKQpsZWdfbGluZV9sYWJzICAgPC0gYyhsaW5lX2JsYWNrLCBsaW5lX2Jyb3duLCBsaW5lX21hZ2VudGEsIGxpbmVfZ3JlZW4sIGxpbmVfcmVkKQpsZWdfbGluZV9icmVha3MgPC0gYygibGluZV9ibGFjayIsICJsaW5lX2Jyb3duIiwgImxpbmVfbWFnZW50YSIsICJsaW5lX2dyZWVuIiwgImxpbmVfcmVkIikKbGVnX2xpbmVfY29scyAgIDwtIGMoImxpbmVfYmxhY2siPSJibGFjayIsICJsaW5lX2Jyb3duIj0iYnJvd24iLCAibGluZV9tYWdlbnRhIj0ibWFnZW50YSIsCiAgICAgICAgICAgICAgICAgICAgICJsaW5lX2dyZWVuIj0iZ3JlZW4iLCAibGluZV9yZWQiPSJyZWQiKQojIGxlZ19saW5lX2xhYnMgICA8LSBjKGxpbmVfYmxhY2ssIGxpbmVfYnJvd24sIGxpbmVfbWFnZW50YSwgbGluZV9ncmVlbiwgbGluZV9ibHVlLCBsaW5lX3JlZCkKIyBsZWdfbGluZV9icmVha3MgPC0gYygibGluZV9ibGFjayIsICJsaW5lX2Jyb3duIiwgImxpbmVfbWFnZW50YSIsICJsaW5lX2dyZWVuIiwgImxpbmVfYmx1ZSIsICJsaW5lX3JlZCIpCiMgbGVnX2xpbmVfY29scyAgIDwtIGMoImxpbmVfYmxhY2siPSJibGFjayIsICJsaW5lX2Jyb3duIj0iYnJvd24iLCAibGluZV9tYWdlbnRhIj0ibWFnZW50YSIsCiMgICAgICAgICAgICAgICAgICAgICAgImxpbmVfZ3JlZW4iPSJncmVlbiIsICJsaW5lX2JsdWUiPSJibHVlIiwgImxpbmVfcmVkIj0icmVkIikKZmlsbF9nIDwtIGJxdW90ZSgiOTAlIHByZWQuIGJhbmQiKQojIGZpbGxfYiA8LSBicXVvdGUoIjk1JSBwcmVkLiBiYW5kIikKZmlsbF9yIDwtIGJxdW90ZSgiOTUlIHByZWQuIGJhbmQiKQpmaWxsX2cgPC0gYnF1b3RlKCI5MCUgcHJlZC4gYmFuZCIpCmZpbGxfYiA8LSBicXVvdGUoIjk1JSBwcmVkLiBiYW5kIikKZmlsbF9yIDwtIGJxdW90ZSgiOTklIHByZWQuIGJhbmQiKQpsZWdfZmlsbF9sYWJzICAgPC0gYyggZmlsbF9nLCBmaWxsX3IpCmxlZ19maWxsX2JyZWFrcyA8LSBjKCJmaWxsX2ciLCAiZmlsbF9yIikKbGVnX2ZpbGxfY29scyAgIDwtIGMoImZpbGxfZyI9ImxpZ2h0Z3JlZW4iLCAiZmlsbF9yIj0ib3JhbmdlcmVkIikKbGVnX2ZpbGxfbGFicyAgIDwtIGMoIGZpbGxfZywgZmlsbF9iLCBmaWxsX3IpCmxlZ19maWxsX2JyZWFrcyA8LSBjKCJmaWxsX2ciLCAiZmlsbF9iIiwgImZpbGxfciIpCmxlZ19maWxsX2NvbHMgICA8LSBjKCJmaWxsX2ciPSJsaWdodGdyZWVuIiwgImZpbGxfYiI9ImJsdWUiLCAiZmlsbF9yIj0ib3JhbmdlcmVkIikKbGVnX2NvbF9sYWJzICAgIDwtIGxlZ19saW5lX2xhYnMKbGVnX2NvbF9icmVha3MgIDwtIGxlZ19saW5lX2JyZWFrcwpsZWdfY29sX2NvbHMgICAgPC0gbGVnX2xpbmVfY29scwp5X3ByZWRfbHAgPC0gZ2dwbG90KERhdGFfZGYsIGFlcyh4PXQpKSArIAogIGdlb21fbGluZShkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPD0gdFtUKzFdKSwgYWVzKHk9c2FsZXNfbG9nLCBjb2xvcj0ibGluZV9ibGFjayIpLAogICAgICAgICAgICBsaW5ldHlwZT0ic29saWQiLCBhbHBoYT0xLCBzaXplPTAuMywgZ3JvdXA9MSkgKwogIGdlb21fbGluZShkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPj0gdFtUKzFdKSwgYWVzKHk9c2FsZXNfbG9nLCBjb2xvcj0ibGluZV9tYWdlbnRhIiksCiAgICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsIGFscGhhPTEsIHNpemU9MC4zLCBncm91cD0xKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhZXMoeT1zYWxlc19sb2dfSFdfcG9pbnRfZm9yICwgY29sb3VyPSJsaW5lX2Jyb3duIiksCiAgICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsIGFscGhhPTEsIHNpemU9MC4zKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhZXMoeT1zYWxlc19sb2dfYm9vdF8wOTVfbG93X0hXX2Zvcl9pbnQsIGNvbG91cj0ibGluZV9yZWQiKSwKICAgICAgICAgICAgbGluZXR5cGU9InNvbGlkIiwgYWxwaGE9MSwgc2l6ZT0wLjMpICsKICBnZW9tX2xpbmUoZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFlcyh5PXNhbGVzX2xvZ19ib290XzA5NV91cHBfSFdfZm9yX2ludCwgY29sb3VyPSJsaW5lX3JlZCIpLAogICAgICAgICAgICBsaW5ldHlwZT0ic29saWQiLCBhbHBoYT0xLCBzaXplPTAuMykgKwogIGdlb21fbGluZShkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPj0gdFtUKzFdKSwgYWVzKHk9c2FsZXNfbG9nX2Jvb3RfMDgwX2xvd19IV19mb3JfaW50LCBjb2xvdXI9ImxpbmVfZ3JlZW4iKSwKICAgICAgICAgICAgbGluZXR5cGU9InNvbGlkIiwgYWxwaGE9MSwgc2l6ZT0wLjMpICsKICBnZW9tX2xpbmUoZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFlcyh5PXNhbGVzX2xvZ19ib290XzA4MF91cHBfSFdfZm9yX2ludCwgY29sb3VyPSJsaW5lX2dyZWVuIiksCiAgICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsIGFscGhhPTEsIHNpemU9MC4zKSArCiAgZ2VvbV9yaWJib24oZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFscGhhPTAuMywgY29sb3VyPSJvcmFuZ2VyZWQiLAogICAgICAgICAgICAgIGFlcyh5bWluPXNhbGVzX2xvZ19ib290XzA5NV9sb3dfSFdfZm9yX2ludCwgeW1heD1zYWxlc19sb2dfYm9vdF8wOTVfdXBwX0hXX2Zvcl9pbnQsIGZpbGw9ImZpbGxfciIpKSArCiAgZ2VvbV9yaWJib24oZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFscGhhPTAuMywgY29sb3VyPSJvcmFuZ2VyZWQiLAogICAgICAgICAgICAgIGFlcyh5bWluPXNhbGVzX2xvZ19ib290XzA5NV9sb3dfSFdfZm9yX2ludCwgeW1heD1zYWxlc19sb2dfYm9vdF8wOTVfdXBwX0hXX2Zvcl9pbnQsIGZpbGw9ImZpbGxfciIpKSArCiAgZ2VvbV9yaWJib24oZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFscGhhPTAuMywgY29sb3VyPSJibHVlIiwKICAgICAgICAgICAgICBhZXMoeW1pbj1zYWxlc19sb2dfYm9vdF8wOTVfbG93X0hXX2Zvcl9pbnQsIHltYXg9c2FsZXNfbG9nX2Jvb3RfMDgwX2xvd19IV19mb3JfaW50LCBmaWxsPSJmaWxsX2IiKSkgKwogIGdlb21fcmliYm9uKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhbHBoYT0wLjMsIGNvbG91cj0iYmx1ZSIsCiAgICAgICAgICAgICAgYWVzKHltaW49c2FsZXNfbG9nX2Jvb3RfMDgwX3VwcF9IV19mb3JfaW50LCB5bWF4PXNhbGVzX2xvZ19ib290XzA5NV91cHBfSFdfZm9yX2ludCwgZmlsbD0iZmlsbF9iIikpICsKICBnZW9tX3JpYmJvbihkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPj0gdFtUKzFdKSwgYWxwaGE9MC4zLCBjb2xvdXI9ImdyZWVuIiwKICAgICAgICAgICAgICBhZXMoeW1pbj1zYWxlc19sb2dfYm9vdF8wODBfbG93X0hXX2Zvcl9pbnQsIHltYXg9c2FsZXNfbG9nX2Jvb3RfMDgwX3VwcF9IV19mb3JfaW50LCBmaWxsPSJmaWxsX2ciKSkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lPXhfbmFtZSwgYnJlYWtzPXhfYnJlYWtzLCBsYWJlbHM9eF9sYWJzLCBsaW1pdHM9eF9saW1zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWU9eV9uYW1lLCBicmVha3M9eV9icmVha3MsIGxhYmVscz1OVUxMLCBsaW1pdHM9eV9saW1zLAogICAgICAgICAgICAgICAgICAgICBzZWMuYXhpcz0gc2VjX2F4aXMofi4sIGJyZWFrcz15X2JyZWFrcywgbGFiZWxzPXlfbGFicykpICsKICBnZ3RpdGxlKHRpdGxlX2NvbnRlbnQpICsKICBsYWJzKHN1YnRpdGxlPXN1YnRpdGxlX2NvbnRlbnQsIGNhcHRpb249Y2FwdGlvbl9jb250ZW50KSArCiAgZ3VpZGVzKGxpbmV0eXBlPSJub25lIiwgc2hhcGU9Im5vbmUiKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lPSJMZWdlbmQiLCBsYWJlbHM9bGVnX2xpbmVfbGFicywgdmFsdWVzPWxlZ19saW5lX2NvbHMsIGJyZWFrcz1sZWdfbGluZV9icmVha3MpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSIiLCBsYWJlbHM9bGVnX2ZpbGxfbGFicywgdmFsdWVzPWxlZ19maWxsX2NvbHMsIGJyZWFrcz1sZWdfZmlsbF9icmVha3MpICsKICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZChvcmRlcj0xKSwgZmlsbD1ndWlkZV9sZWdlbmQob3JkZXI9MikpICsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksIAogICAgICAgIHBsb3Quc3VidGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0ID0gIDAuNSksCiAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMS4wKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT0tNDUsIHZqdXN0PTEsIGhqdXN0PS0wLjMpLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuOCwiY20iKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpwbG90KHlfcHJlZF9scCkKYGBgCiMjIyMgVG90YWwgZm9yZWNhc3Qgd2l0aCAqR3JhbiBNZWFuIG1ldGhvZCoKCklsIHRlcnpvICpmb3JlY2FzdCogw6ggb3R0ZW51dG8gdW5lbmRvICIqR3JhbiBNZWFuIHRyZW5kIG1ldGhvZCoiICsgIipuYWl2ZSBzZWFzb25hbGl0eSBtZXRob2QqIiArICIqYXJpbWEoNCwwLDYpIHJlbWFpbmRlcioiCmBgYHtyfQojaGVhZChkZikKc2FsZXNfbG9nIDwtIGFzLnZlY3Rvcihsb2coZGYkc2FsZXMpKQpzYWxlc19sb2dfdGVzdCA8LSBzYWxlc19sb2dbYygoVHJuU19sZW5ndGgrMSk6KFRyblNfbGVuZ3RoK1RzdFNfbGVuZ3RoKSldCnlfcmVtX3ByZWRfZm9yX21lYW4gPC0gYXMudmVjdG9yKHlfcmVtX3ByZWRfZm9yX21lYW4pCnNhbGVzX2xvZ19ncmFuX21lYW5fcG9pbnRfcHJlZCA8LSB5X2dyYW5fbWVhbl9wcmVkICsgeV9zZWFzX25haXZlX3ByZWQgKyB5X3JlbV9wcmVkX2Zvcl9tZWFuCgpzYWxlc19sb2dfZ3Jhbl9tZWFuX3BvaW50X2ZvciA8LSBjKHNhbGVzX2xvZ1tjKDE6VHJuU19sZW5ndGgpXSwgc2FsZXNfbG9nX2dyYW5fbWVhbl9wb2ludF9wcmVkKQp5X2xvZ19ncmFuX21lYW5fcHJlZF9SSF9mb3JfMDgwX2xvd19pbnQgPC0geV9ncmFuX21lYW5fcHJlZCArIHlfc2Vhc19uYWl2ZV9wcmVkICArIHlfcmVzX2xvZ19wcmVkX1JIX2Zvcl8wODBfbG93X2ludAp5X2xvZ19ncmFuX21lYW5fcHJlZF9SSF9mb3JfMDgwX3VwcF9pbnQgPC0geV9ncmFuX21lYW5fcHJlZCArIHlfc2Vhc19uYWl2ZV9wcmVkICArIHlfcmVzX2xvZ19wcmVkX1JIX2Zvcl8wODBfdXBwX2ludAp5X2xvZ19ncmFuX21lYW5fcHJlZF9SSF9mb3JfMDk1X2xvd19pbnQgPC0geV9ncmFuX21lYW5fcHJlZCArIHlfc2Vhc19uYWl2ZV9wcmVkICArIHlfcmVzX2xvZ19wcmVkX1JIX2Zvcl8wOTVfbG93X2ludAp5X2xvZ19ncmFuX21lYW5fcHJlZF9SSF9mb3JfMDk1X3VwcF9pbnQgPC0geV9ncmFuX21lYW5fcHJlZCArIHlfc2Vhc19uYWl2ZV9wcmVkICArIHlfcmVzX2xvZ19wcmVkX1JIX2Zvcl8wOTVfdXBwX2ludAoKc2FsZXNfbG9nX2Jvb3RfMDgwX2xvd19ncmFuX21lYW5fZm9yX2ludCA8LSBjKHJlcChOQSxUcm5TX2xlbmd0aCkseV9sb2dfZ3Jhbl9tZWFuX3ByZWRfUkhfZm9yXzA4MF9sb3dfaW50KQpzYWxlc19sb2dfYm9vdF8wODBfdXBwX2dyYW5fbWVhbl9mb3JfaW50IDwtIGMocmVwKE5BLFRyblNfbGVuZ3RoKSx5X2xvZ19ncmFuX21lYW5fcHJlZF9SSF9mb3JfMDgwX3VwcF9pbnQpCnNhbGVzX2xvZ19ib290XzA5NV9sb3dfZ3Jhbl9tZWFuX2Zvcl9pbnQgPC0gYyhyZXAoTkEsVHJuU19sZW5ndGgpLHlfbG9nX2dyYW5fbWVhbl9wcmVkX1JIX2Zvcl8wOTVfbG93X2ludCkKc2FsZXNfbG9nX2Jvb3RfMDk1X3VwcF9ncmFuX21lYW5fZm9yX2ludCA8LSBjKHJlcChOQSxUcm5TX2xlbmd0aCkseV9sb2dfZ3Jhbl9tZWFuX3ByZWRfUkhfZm9yXzA5NV91cHBfaW50KQoKc2FsZXNfZ3Jhbl9tZWFuX3ByZWRfZGYgPC0gYWRkX2NvbHVtbihkZiwgc2FsZXNfbG9nPXNhbGVzX2xvZywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsZXNfbG9nX2dyYW5fbWVhbl9wb2ludF9mb3I9c2FsZXNfbG9nX2dyYW5fbWVhbl9wb2ludF9mb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsZXNfbG9nX2Jvb3RfMDgwX2xvd19ncmFuX21lYW5fZm9yX2ludD1zYWxlc19sb2dfYm9vdF8wODBfbG93X2dyYW5fbWVhbl9mb3JfaW50LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxlc19sb2dfYm9vdF8wODBfdXBwX2dyYW5fbWVhbl9mb3JfaW50PXNhbGVzX2xvZ19ib290XzA4MF91cHBfZ3Jhbl9tZWFuX2Zvcl9pbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsZXNfbG9nX2Jvb3RfMDk1X2xvd19ncmFuX21lYW5fZm9yX2ludD1zYWxlc19sb2dfYm9vdF8wOTVfbG93X2dyYW5fbWVhbl9mb3JfaW50LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxlc19sb2dfYm9vdF8wOTVfdXBwX2dyYW5fbWVhbl9mb3JfaW50PXNhbGVzX2xvZ19ib290XzA5NV91cHBfZ3Jhbl9tZWFuX2Zvcl9pbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmFmdGVyPSJzYWxlcyIpCmBgYAoKVmllbmUgaW5pemlhbG1lbnRlIGVmZmV0dHVhdG8gdW4gcGxvdCBpbiBjdWkgaW4gY3VpIHZpZW5lIG1lc3NvIGEgY29uZnJvbnRvIGlsICpwYXRoKiByZWFsZSBkZWkgZGF0aSBjb24gcXVlbGxvIHByZWRldHRvLgpgYGB7cn0KYXV0b3Bsb3QodHMobG9nKGRmX0NhbXJ5X1VTX3NhbGVzJHNhbGVzKSwgc3RhcnQ9YygyMDA5LCAwMSksIGVuZD1jKDIwMjIsIDExKSwgZnJlcXVlbmN5ID0gMTIpLCBzZXJpZXMgPSAiVG95b3RhIENhbXJ5IHJlYWwgcGF0aCIpICsKICBhdXRvbGF5ZXIodHMoc2FsZXNfbG9nX2dyYW5fbWVhbl9wb2ludF9wcmVkLCBzdGFydD1jKDIwMjEsIDEyKSwgZW5kPWMoMjAyMiwgMTEpLCBmcmVxdWVuY3kgPSAxMiksIHNlcmllcz0iRm9yZWNhc3Qgd2l0aCBHcmFuLU1lYW4gdHJlbmQiKSArCiAgeGxhYigiTW9udGgiKSArIHlsYWIoIlNhbGVzIikgKwogIGdndGl0bGUoIkZvcmVjYXN0IFRveW90YSBDYW1yeSBzYWxlcyIpICsKICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCh0aXRsZT0iTGVnZW5kIikpCmBgYApQZXIgcG9pIHBhc3NhcmUgYSB1biBwbG90IHBpw7kgY29tcGxldG8gaW4gY3VpIHNvbm8gcHJlc2VudGkgZ2xpIGludGVydmFsbGkgZGkgcHJlZGl6aW9uZSBkYXRpIGRhbGxvIHN0dWRpbyBkZWkgcmVzaWR1aSBkZWwgbW9kZWxsbyAqQVJNQSouCmBgYHtyfQpEYXRhX2RmIDwtIHNhbGVzX2dyYW5fbWVhbl9wcmVkX2RmCmxlbmd0aCA8LSBucm93KERhdGFfZGYpClQgPC0gVHJuU19sZW5ndGgKdGl0bGVfY29udGVudCA8LSBicXVvdGUoYXRvcCgiTGluZSBQbG90IG9mIHRoZSBUb3lvdGEgQ2Ftcnkgc2FsZXMgVHJhaW5pbmcgU2V0IGFuZCBQcmVkaWN0ZWQgVGVzdCBTZXRzIC0gZnJvbSAiLCAuKEZpcnN0X0RhdGUpLCAiIHRvICIsIC4oTGFzdF9EYXRlKSkpCnN1YnRpdGxlX2NvbnRlbnQgPC0gYnF1b3RlKHBhc3RlKCJUcmFpbmluZyBzZXQgbGVuZ3RoICIsIC4oVHJuU19sZW5ndGgpLCAiIHNhbXBsZSBwb2ludHMuIFRlc3Qgc2V0IGxlbmd0aCAiLCAuKFRzdFNfbGVuZ3RoKSwgIiBzYW1wbGUgcG9pbnRzLiIpKQpjYXB0aW9uX2NvbnRlbnQgPC0gKCJBdXRob3I6IE1hdHRlbyBDaGlhY2NoaWEiKQp4X25hbWUgPC0gYnF1b3RlKCIiKQojIGxpYnJhcnkobnVtYmVycykKIyBwcmltZUZhY3RvcnMoVCkKeF9icmVha3NfbnVtIDwtIDMzCnhfYnJlYWtzX21pbiA8LSBEYXRhX2RmJHRbMV0KeF9icmVha3NfbWF4IDwtIERhdGFfZGYkdFtsZW5ndGhdCnhfYmlud2lkdGggPC0gZmxvb3IoKHhfYnJlYWtzX21heC14X2JyZWFrc19taW4pL3hfYnJlYWtzX251bSkKeF9icmVha3MgPC0gc2VxKGZyb209eF9icmVha3NfbWluLCB0bz14X2JyZWFrc19tYXgsIGJ5PXhfYmlud2lkdGgpCmlmKCh4X2JyZWFrc19tYXgtbWF4KHhfYnJlYWtzKSk+eF9iaW53aWR0aC8yKXt4X2JyZWFrcyA8LSBjKHhfYnJlYWtzLHhfYnJlYWtzX21heCl9CnhfbGFicyA8LSBwYXN0ZShEYXRhX2RmJE1vbnRoW3hfYnJlYWtzXSxEYXRhX2RmJFllYXJbeF9icmVha3NdKQpKIDwtIDAKeF9saW1zIDwtIGMoeF9icmVha3NfbWluLUoqeF9iaW53aWR0aCwgeF9icmVha3NfbWF4K0oqeF9iaW53aWR0aCkKeV9uYW1lIDwtIGJxdW90ZSgiU2FsZXMiKQp5X2JyZWFrc19udW0gPC0gMTAKeV9tYXggPC0gbWF4KG5hLm9taXQoRGF0YV9kZiRzYWxlc19sb2cpLG5hLm9taXQoRGF0YV9kZiRzYWxlc19sb2dfYm9vdF8wOTVfdXBwX2dyYW5fbWVhbl9mb3JfaW50KSkKeV9taW4gPC0gbWluKG5hLm9taXQoRGF0YV9kZiRzYWxlc19sb2cpLG5hLm9taXQoRGF0YV9kZiRzYWxlc19sb2dfYm9vdF8wOTVfbG93X2dyYW5fbWVhbl9mb3JfaW50KSkKeV9iaW53aWR0aCA8LSByb3VuZCgoeV9tYXgteV9taW4pL3lfYnJlYWtzX251bSwgZGlnaXRzPTMpCnlfYnJlYWtzX2xvdyA8LSBmbG9vcih5X21pbi95X2JpbndpZHRoKSp5X2JpbndpZHRoCnlfYnJlYWtzX3VwIDwtIGNlaWxpbmcoeV9tYXgveV9iaW53aWR0aCkqeV9iaW53aWR0aAp5X2JyZWFrcyA8LSByb3VuZChzZXEoZnJvbT15X2JyZWFrc19sb3csIHRvPXlfYnJlYWtzX3VwLCBieT15X2JpbndpZHRoKSxkaWdpdHM9MykKeV9sYWJzIDwtIGZvcm1hdCh5X2JyZWFrcywgc2NpZW50aWZpYz1GQUxTRSkKSyA8LSAwCnlfbGltcyA8LSBjKCh5X2JyZWFrc19sb3ctSyp5X2JpbndpZHRoKSwgKHlfYnJlYWtzX3VwK0sqeV9iaW53aWR0aCkpCmxpbmVfYmxhY2sgICA8LSBicXVvdGUoImluLXNhbXBsZSBwYXRoIikKbGluZV9tYWdlbnRhIDwtIGJxdW90ZSgicmVhbCBwYXRoIikKbGluZV9icm93biAgIDwtIGJxdW90ZSgicHJlZGljdGVkIHBhdGgiKQpsaW5lX2dyZWVuICAgPC0gYnF1b3RlKCI4MCUgcHJlZC5pbnQuIikKIyBsaW5lX2JsdWUgICAgPC0gYnF1b3RlKCI5NSUgcHJlZC5pbnQuIikKbGluZV9yZWQgICAgIDwtIGJxdW90ZSgiOTUlIHByZWQuaW50LiIpCmxlZ19saW5lX2xhYnMgICA8LSBjKGxpbmVfYmxhY2ssIGxpbmVfYnJvd24sIGxpbmVfbWFnZW50YSwgbGluZV9ncmVlbiwgbGluZV9yZWQpCmxlZ19saW5lX2JyZWFrcyA8LSBjKCJsaW5lX2JsYWNrIiwgImxpbmVfYnJvd24iLCAibGluZV9tYWdlbnRhIiwgImxpbmVfZ3JlZW4iLCAibGluZV9yZWQiKQpsZWdfbGluZV9jb2xzICAgPC0gYygibGluZV9ibGFjayI9ImJsYWNrIiwgImxpbmVfYnJvd24iPSJicm93biIsICJsaW5lX21hZ2VudGEiPSJtYWdlbnRhIiwKICAgICAgICAgICAgICAgICAgICAgImxpbmVfZ3JlZW4iPSJncmVlbiIsICJsaW5lX3JlZCI9InJlZCIpCiMgbGVnX2xpbmVfbGFicyAgIDwtIGMobGluZV9ibGFjaywgbGluZV9icm93biwgbGluZV9tYWdlbnRhLCBsaW5lX2dyZWVuLCBsaW5lX2JsdWUsIGxpbmVfcmVkKQojIGxlZ19saW5lX2JyZWFrcyA8LSBjKCJsaW5lX2JsYWNrIiwgImxpbmVfYnJvd24iLCAibGluZV9tYWdlbnRhIiwgImxpbmVfZ3JlZW4iLCAibGluZV9ibHVlIiwgImxpbmVfcmVkIikKIyBsZWdfbGluZV9jb2xzICAgPC0gYygibGluZV9ibGFjayI9ImJsYWNrIiwgImxpbmVfYnJvd24iPSJicm93biIsICJsaW5lX21hZ2VudGEiPSJtYWdlbnRhIiwKIyAgICAgICAgICAgICAgICAgICAgICAibGluZV9ncmVlbiI9ImdyZWVuIiwgImxpbmVfYmx1ZSI9ImJsdWUiLCAibGluZV9yZWQiPSJyZWQiKQpmaWxsX2cgPC0gYnF1b3RlKCI5MCUgcHJlZC4gYmFuZCIpCiMgZmlsbF9iIDwtIGJxdW90ZSgiOTUlIHByZWQuIGJhbmQiKQpmaWxsX3IgPC0gYnF1b3RlKCI5NSUgcHJlZC4gYmFuZCIpCmZpbGxfZyA8LSBicXVvdGUoIjkwJSBwcmVkLiBiYW5kIikKZmlsbF9iIDwtIGJxdW90ZSgiOTUlIHByZWQuIGJhbmQiKQpmaWxsX3IgPC0gYnF1b3RlKCI5OSUgcHJlZC4gYmFuZCIpCmxlZ19maWxsX2xhYnMgICA8LSBjKCBmaWxsX2csIGZpbGxfcikKbGVnX2ZpbGxfYnJlYWtzIDwtIGMoImZpbGxfZyIsICJmaWxsX3IiKQpsZWdfZmlsbF9jb2xzICAgPC0gYygiZmlsbF9nIj0ibGlnaHRncmVlbiIsICJmaWxsX3IiPSJvcmFuZ2VyZWQiKQpsZWdfZmlsbF9sYWJzICAgPC0gYyggZmlsbF9nLCBmaWxsX2IsIGZpbGxfcikKbGVnX2ZpbGxfYnJlYWtzIDwtIGMoImZpbGxfZyIsICJmaWxsX2IiLCAiZmlsbF9yIikKbGVnX2ZpbGxfY29scyAgIDwtIGMoImZpbGxfZyI9ImxpZ2h0Z3JlZW4iLCAiZmlsbF9iIj0iYmx1ZSIsICJmaWxsX3IiPSJvcmFuZ2VyZWQiKQpsZWdfY29sX2xhYnMgICAgPC0gbGVnX2xpbmVfbGFicwpsZWdfY29sX2JyZWFrcyAgPC0gbGVnX2xpbmVfYnJlYWtzCmxlZ19jb2xfY29scyAgICA8LSBsZWdfbGluZV9jb2xzCnlfcHJlZF9scCA8LSBnZ3Bsb3QoRGF0YV9kZiwgYWVzKHg9dCkpICsgCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA8PSB0W1QrMV0pLCBhZXMoeT1zYWxlc19sb2csIGNvbG9yPSJsaW5lX2JsYWNrIiksCiAgICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsIGFscGhhPTEsIHNpemU9MC4zLCBncm91cD0xKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhZXMoeT1zYWxlc19sb2csIGNvbG9yPSJsaW5lX21hZ2VudGEiKSwKICAgICAgICAgICAgbGluZXR5cGU9InNvbGlkIiwgYWxwaGE9MSwgc2l6ZT0wLjMsIGdyb3VwPTEpICsKICBnZW9tX2xpbmUoZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFlcyh5PXNhbGVzX2xvZ19ncmFuX21lYW5fcG9pbnRfZm9yICwgY29sb3VyPSJsaW5lX2Jyb3duIiksCiAgICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsIGFscGhhPTEsIHNpemU9MC4zKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhZXMoeT1zYWxlc19sb2dfYm9vdF8wOTVfbG93X2dyYW5fbWVhbl9mb3JfaW50LCBjb2xvdXI9ImxpbmVfcmVkIiksCiAgICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsIGFscGhhPTEsIHNpemU9MC4zKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhZXMoeT1zYWxlc19sb2dfYm9vdF8wOTVfdXBwX2dyYW5fbWVhbl9mb3JfaW50LCBjb2xvdXI9ImxpbmVfcmVkIiksCiAgICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsIGFscGhhPTEsIHNpemU9MC4zKSArCiAgZ2VvbV9saW5lKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhZXMoeT1zYWxlc19sb2dfYm9vdF8wODBfbG93X2dyYW5fbWVhbl9mb3JfaW50LCBjb2xvdXI9ImxpbmVfZ3JlZW4iKSwKICAgICAgICAgICAgbGluZXR5cGU9InNvbGlkIiwgYWxwaGE9MSwgc2l6ZT0wLjMpICsKICBnZW9tX2xpbmUoZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFlcyh5PXNhbGVzX2xvZ19ib290XzA4MF91cHBfZ3Jhbl9tZWFuX2Zvcl9pbnQsIGNvbG91cj0ibGluZV9ncmVlbiIpLAogICAgICAgICAgICBsaW5ldHlwZT0ic29saWQiLCBhbHBoYT0xLCBzaXplPTAuMykgKwogIGdlb21fcmliYm9uKGRhdGE9c3Vic2V0KERhdGFfZGYsIERhdGFfZGYkdCA+PSB0W1QrMV0pLCBhbHBoYT0wLjMsIGNvbG91cj0ib3JhbmdlcmVkIiwKICAgICAgICAgICAgICBhZXMoeW1pbj1zYWxlc19sb2dfYm9vdF8wOTVfbG93X2dyYW5fbWVhbl9mb3JfaW50LCB5bWF4PXNhbGVzX2xvZ19ib290XzA5NV91cHBfZ3Jhbl9tZWFuX2Zvcl9pbnQsIGZpbGw9ImZpbGxfciIpKSArCiAgZ2VvbV9yaWJib24oZGF0YT1zdWJzZXQoRGF0YV9kZiwgRGF0YV9kZiR0ID49IHRbVCsxXSksIGFscGhhPTAuMywgY29sb3VyPSJvcmFuZ2VyZWQiLAogICAgICAgICAgICAgIGFlcyh5bWluPXNhbGVzX2xvZ19ib290XzA5NV9sb3dfZ3Jhbl9tZWFuX2Zvcl9pbnQsIHltYXg9c2FsZXNfbG9nX2Jvb3RfMDk1X3VwcF9ncmFuX21lYW5fZm9yX2ludCwgZmlsbD0iZmlsbF9yIikpICsKICBnZW9tX3JpYmJvbihkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPj0gdFtUKzFdKSwgYWxwaGE9MC4zLCBjb2xvdXI9ImJsdWUiLAogICAgICAgICAgICAgIGFlcyh5bWluPXNhbGVzX2xvZ19ib290XzA5NV9sb3dfZ3Jhbl9tZWFuX2Zvcl9pbnQsIHltYXg9c2FsZXNfbG9nX2Jvb3RfMDgwX2xvd19ncmFuX21lYW5fZm9yX2ludCwgZmlsbD0iZmlsbF9iIikpICsKICBnZW9tX3JpYmJvbihkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPj0gdFtUKzFdKSwgYWxwaGE9MC4zLCBjb2xvdXI9ImJsdWUiLAogICAgICAgICAgICAgIGFlcyh5bWluPXNhbGVzX2xvZ19ib290XzA4MF91cHBfZ3Jhbl9tZWFuX2Zvcl9pbnQsIHltYXg9c2FsZXNfbG9nX2Jvb3RfMDk1X3VwcF9ncmFuX21lYW5fZm9yX2ludCwgZmlsbD0iZmlsbF9iIikpICsKICBnZW9tX3JpYmJvbihkYXRhPXN1YnNldChEYXRhX2RmLCBEYXRhX2RmJHQgPj0gdFtUKzFdKSwgYWxwaGE9MC4zLCBjb2xvdXI9ImdyZWVuIiwKICAgICAgICAgICAgICBhZXMoeW1pbj1zYWxlc19sb2dfYm9vdF8wODBfbG93X2dyYW5fbWVhbl9mb3JfaW50LCB5bWF4PXNhbGVzX2xvZ19ib290XzA4MF91cHBfZ3Jhbl9tZWFuX2Zvcl9pbnQsIGZpbGw9ImZpbGxfZyIpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9eF9uYW1lLCBicmVha3M9eF9icmVha3MsIGxhYmVscz14X2xhYnMsIGxpbWl0cz14X2xpbXMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZT15X25hbWUsIGJyZWFrcz15X2JyZWFrcywgbGFiZWxzPU5VTEwsIGxpbWl0cz15X2xpbXMsCiAgICAgICAgICAgICAgICAgICAgIHNlYy5heGlzPSBzZWNfYXhpcyh+LiwgYnJlYWtzPXlfYnJlYWtzLCBsYWJlbHM9eV9sYWJzKSkgKwogIGdndGl0bGUodGl0bGVfY29udGVudCkgKwogIGxhYnMoc3VidGl0bGU9c3VidGl0bGVfY29udGVudCwgY2FwdGlvbj1jYXB0aW9uX2NvbnRlbnQpICsKICBndWlkZXMobGluZXR5cGU9Im5vbmUiLCBzaGFwZT0ibm9uZSIpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWU9IkxlZ2VuZCIsIGxhYmVscz1sZWdfbGluZV9sYWJzLCB2YWx1ZXM9bGVnX2xpbmVfY29scywgYnJlYWtzPWxlZ19saW5lX2JyZWFrcykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IiIsIGxhYmVscz1sZWdfZmlsbF9sYWJzLCB2YWx1ZXM9bGVnX2ZpbGxfY29scywgYnJlYWtzPWxlZ19maWxsX2JyZWFrcykgKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKG9yZGVyPTEpLCBmaWxsPWd1aWRlX2xlZ2VuZChvcmRlcj0yKSkgKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgCiAgICAgICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoaGp1c3QgPSAgMC41KSwKICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxLjApLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPS00NSwgdmp1c3Q9MSwgaGp1c3Q9LTAuMyksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMC44LCJjbSIpLCBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpCnBsb3QoeV9wcmVkX2xwKQpgYGAKIyMjIDMuIEFjY3VyYWN5CgpVbmEgdm9sdGEgdXRpbGl6emF0aSBpIGRpdmVyc2kgbWV0b2RpIGRpICpmb3JlY2FzdCogc2kgcGFzc2EgYWxsJ2FuYWxpc2kgZGVpIHJpc3VsdGF0aS4gCmBgYHtyfQphdXRvcGxvdCh0cyhsb2coZGZfQ2FtcnlfVVNfc2FsZXMkc2FsZXMpLCBzdGFydD1jKDIwMDksIDAxKSwgZW5kPWMoMjAyMiwgMTEpLCBmcmVxdWVuY3kgPSAxMiksIHNlcmllcyA9ICJSZWFsIFBhdGgiKSArCiAgYXV0b2xheWVyKHRzKHNhbGVzX2xvZ19ncmFuX21lYW5fcG9pbnRfcHJlZCwgc3RhcnQ9YygyMDIxLCAxMiksIGVuZD1jKDIwMjIsIDExKSwgZnJlcXVlbmN5ID0gMTIpLCBzZXJpZXM9IkdyYW5NZWFuIikrCiAgYXV0b2xheWVyKHRzKHNhbGVzX2xvZ19uYWl2ZV9wb2ludF9wcmVkLCBzdGFydD1jKDIwMjEsIDEyKSwgZW5kPWMoMjAyMiwgMTEpLCBmcmVxdWVuY3kgPSAxMiksIHNlcmllcz0iTmFpdmUiKSsKICBhdXRvbGF5ZXIodHMoc2FsZXNfbG9nX0hXX3BvaW50X3ByZWQsIHN0YXJ0PWMoMjAyMSwgMTIpLCBlbmQ9YygyMDIyLCAxMSksIGZyZXF1ZW5jeSA9IDEyKSwgc2VyaWVzPSJIVyIpKwogIHhsYWIoIk1vbnRoIikgKyB5bGFiKCJTYWxlcyIpICsKICBnZ3RpdGxlKCJGb3JlY2FzdHMgVG95b3RhIENhbXJ5IHNhbGVzIikgKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJGb3JlY2FzdCIpKQpgYGAKTWV0dGVuZG8gYSBjb25mcm9udG8gaSAqbGluZXBsb3QqIGRlbGxlIHByZWRpemlvbmkgc2kgbm90YSBzb3N0YW56aWFsbWVudGUgY2hlIGlsIG1ldG9kbyBjb24gbGEgKmdyYW4gbWVhbiogdGVuZGUgYSBzb3ZyYXN0aW1hcmUgaSB2YWxvcmkgZGkgY29uc2VndWVuemEgc2VtYnJhIGVzc2VyZSBpbCBwZWdnaW9yZS4KUGVyIHF1YW50byByaWd1YXJkYSAqTmFpdmUqIGUgKkhXKiBzaSBub3RhIGNoZSBxdWVzdCB1bHRpbW8sIGNoZSBpbml6aWFsbWVudGUgc2VtYnJhIGF2ZXJlIHVuICpwYXRoKiBtb2x0byB2aWNpbm8gYSBxdWVsbG8gcmVhbGUsIHBvaSBjb21pbmNpYSBhIHNvdHRvc3RpbWFyZSBmb3J0ZW1lbnRlIGkgdmFsb3JpLiAqTmFpdmUqIGludmVjZSBzZW1icmEgYXZlcmUgdW4gbWlnbGlvciBjb21wcm9tZXNzbyBkaSBzdGltYS4KClBlciB2YWx1dGFyZSBudW1lcmljYW1lbnRlIGxhIGJvbnTDoCBkZWxsZSBwcmV2aXNpb25lIHZlcnJhbm8gdXRpbGl6emF0ZSBsZSBzZWd1ZW50aSBzdGF0aXN0aWNoZSBkaSBhY2N1cmF0ZXp6YSwgY2hlIHZlcnJhbm5vIHBvaSBtZXNzZSBhIGNvbmZyb250bzoKCi0gKipNRSoqIChNZWFuIEVycm9yKTogcmFwcHJlc2VudGEgbGEgbWVkaWEgZGVnbGkgZXJyb3JpIGRlbCBtb2RlbGxvLiAKJE1BRSA9ICgxL04pIFxzdW1fe2k9MX1ee059IFxvdmVybGluZXt5fV90IC0geV90JAoKLSAqKlJNU0UqKiAoUm9vdCBNZWFuIFNxdWFyZWQgRXJyb3IpOiByYXBwcmVzZW50YSBsYSByYWRpY2UgcXVhZHJhdGEgZGVsbGEgbWVkaWEgZGVpIHF1YWRyYXRpIGRlZ2xpIGVycm9yaSBkZWwgbW9kZWxsby4gSW4gYWx0cmUgcGFyb2xlLCBwdcOyIGVzc2VyZSBjb25zaWRlcmF0byBjb21lIHVuYSBzb3J0YSBkaSBkaXN0YW56YSAobm9ybWFsaXp6YXRhKSB0cmEgaWwgdmV0dG9yZSBkZWkgdmFsb3JpIHByZXZpc3RpIGUgaWwgdmV0dG9yZSBkZWkgdmFsb3JpIG9zc2VydmF0aS4KJFJNU0U9IFxzcXJ0eygxL04pXHN1bV97aT0xfV57Tn0oXG92ZXJsaW5le3l9X3QgLSB5X3QpXjJ9JAoKLSAqKk1BRSoqIChNZWFuIEFic29sdXRlIEVycm9yKTogcmFwcHJlc2VudGEgbGEgbWVkaWEgZGVnbGkgZXJyb3JpIGFzc29sdXRpIGRlbCBtb2RlbGxvLiDDiCB1bmEgbWV0cmljYSBzZW1wbGljZSBlIGZhY2lsbWVudGUgaW50ZXJwcmV0YWJpbGUsIG1hIHB1w7IgZXNzZXJlIGluZmx1ZW56YXRhIGRhIHZhbG9yaSBlc3RyZW1pICgqb3V0bGllcnMqKSBuZWxsYSBkaXN0cmlidXppb25lIGRlaSBkYXRpLgokTUFFID0gKDEvTikgXHN1bV97aT0xfV57Tn0gfFxvdmVybGluZXt5fV90IC0geV90fCQKCi0gKipNUEUqKiAoTWVhbiBQZXJjZW50YWdlIEVycm9yKTogcmFwcHJlc2VudGEgbGEgbWVkaWEgcGVyY2VudHVhbGUgZGVnbGkgZXJyb3JpIGRlbCBtb2RlbGxvLiBQb2ljaMOpIG5lbGxhIGZvcm11bGEgc29ubyB1dGlsaXp6YXRpIGkgdmFsb3JpIGVmZmV0dGl2aSBpbnZlY2UgZGVpIHZhbG9yaSBhc3NvbHV0aSBkZWdsaSBlcnJvcmkgZGkgcHJldmlzaW9uZSwgZ2xpIGVycm9yaSBkaSBwcmV2aXNpb25lIHBvc2l0aXZpIGUgbmVnYXRpdmkgcG9zc29ubyBjb21wZW5zYXJzaSBhIHZpY2VuZGE7IGRpIGNvbnNlZ3VlbnphLCBsYSBmb3JtdWxhIHB1w7IgZXNzZXJlIHV0aWxpenphdGEgY29tZSBtaXN1cmEgZGVsICpiaWFzKiBuZWxsZSBwcmV2aXNpb25pLgokTVBFPSAoMS9OKShcc3VtX3tpPTF9XntOfSh5X3QtXG92ZXJsaW5le3l9X3QpL3lfdCkgICoxMDAkCgotICoqTUFQRSoqIChNZWFuIEFic29sdXRlIFBlcmNlbnRhZ2UgRXJyb3IpOiByYXBwcmVzZW50YSBsYSBtZWRpYSBwZXJjZW50dWFsZSBkZWdsaSBlcnJvcmkgYXNzb2x1dGkgZGVsIG1vZGVsbG8uIEluIGFsdHJlIHBhcm9sZSwgw6ggbGEgbWVkaWEgZGVpIHZhbG9yaSBhc3NvbHV0aSBkZWxsZSBkaWZmZXJlbnplIHBlcmNlbnR1YWxpIHRyYSBsZSBwcmV2aXNpb25pIGRlbCBtb2RlbGxvIGUgaSB2YWxvcmkgZWZmZXR0aXZpLgokTUFQRSA9ICgxL04pKFxzdW1fe2k9MX1ee059fHlfdC1cb3ZlcmxpbmV7eX1fdHwveV90KSoxMDAkCgotICoqTUFTRSoqCgpMYSBzdGF0aXN0aWNhICpNQVNFKiAoTWVhbiBBYnNvbHV0ZSBTY2FsZWQgRXJyb3IpIMOoIHVuYSBtZXRyaWNhIGRpIGFjY3VyYXRlenphIHV0aWxpenphdGEgcGVyIHZhbHV0YXJlIGxhIGJvbnTDoCBkaSB1biBtb2RlbGxvIGRpIHByZXZpc2lvbmUgcmlzcGV0dG8gYSB1bmEgcHJldmlzaW9uZSAqbmFpdmUuKiBMYSBwcmV2aXNpb25lICpuYWl2ZSogw6ggdW4gc2VtcGxpY2UgbW9kZWxsbyBkaSBwcmV2aXNpb25lIGNoZSBzaSBiYXNhIHN1bGxhIHJpcGV0aXppb25lIGRlbCB2YWxvcmUgZGVsbCd1bHRpbWEgb3NzZXJ2YXppb25lIGNvbWUgcHJldmlzaW9uZSBwZXIgbGUgZnV0dXJlIG9zc2VydmF6aW9uaS4KCkxhIE1BU0Ugw6ggZGVmaW5pdGEgY29tZSBpbCByYXBwb3J0byB0cmEgbCdlcnJvcmUgbWVkaW8gYXNzb2x1dG8gZGVsIG1vZGVsbG8gZSBsJ2Vycm9yZSBtZWRpbyBhc3NvbHV0byBkZWwgbW9kZWxsbyBkaSBwcmV2aXNpb25lICpuYWl2ZSogSW4gZm9ybXVsYToKCiRNQVNFID0gIChNQUVfKG1vZGVsKSkgLyAoTUFFXyhuYWl2ZSkpJAoKTGEgKk1BU0UqIMOoIHVuYSBtZXRyaWNhIHV0aWxlIHBlcmNow6kgw6ggaW5kaXBlbmRlbnRlIGRhbGxlIHVuaXTDoCBkaSBtaXN1cmEgZGVsbGEgdmFyaWFiaWxlIGNoZSBzaSBzdGEgcHJldmVkZW5kby4gSW5vbHRyZSwgbGEgTUFTRSDDqCB1bmEgbWV0cmljYSByZWxhdGl2YSwgb3Z2ZXJvIHRpZW5lIGNvbnRvIGRlbGwnZXJyb3JlIGRpIHByZXZpc2lvbmUgZGVsIG1vZGVsbG8gcmlzcGV0dG8gYSB1bmEgcHJldmlzaW9uZSBuYWl2ZSwgY2hlIHJhcHByZXNlbnRhIHVuIG1vZGVsbG8gZGkgYmFzZSBzZW1wbGljZSBtYSBjb211bnF1ZSB2YWxpZG8uCgpJbiBnZW5lcmFsZSwgdW4gdmFsb3JlIGRpIE1BU0UgaW5mZXJpb3JlIGEgMSBpbmRpY2EgY2hlIGlsIG1vZGVsbG8gZGkgcHJldmlzaW9uZSDDqCBtaWdsaW9yZSBkZWxsYSBwcmV2aXNpb25lIG5haXZlLCBtZW50cmUgdW4gdmFsb3JlIGRpIE1BU0Ugc3VwZXJpb3JlIGEgMSBpbmRpY2EgY2hlIGlsIG1vZGVsbG8gZGkgcHJldmlzaW9uZSDDqCBwZWdnaW9yZSBkZWxsYSBwcmV2aXNpb25lIG5haXZlIExhIE1BU0UgcHXDsiBlc3NlcmUgdXRpbGl6emF0YSBwZXIgY29uZnJvbnRhcmUgbGEgYm9udMOgIGRpIGRpdmVyc2kgbW9kZWxsaSBkaSBwcmV2aXNpb25lLCBvIHBlciB2YWx1dGFyZSBsZSBwZXJmb3JtYW5jZSBkaSB1biBtb2RlbGxvIGRpIHByZXZpc2lvbmUgcmlzcGV0dG8gYSB1bmEgcHJldmlzaW9uZSBuYWl2ZSBkaSByaWZlcmltZW50by4KCioqTi5CKio6IGxhIGZ1bnppb25lICIqYWNjdXJhY3kqIiBkZWxsYSBsaWJyZXJpYSAqZm9yZWNhc3QqIGVmZmV0dHVhIGlsIGNhbGNvbG8gdXRpbGl6emFuZG8gJFt5X3QtXG92ZXJsaW5le3l9X3RdJCBpbnZlY2UgZGkgJFtcb3ZlcmxpbmV7eX1fdCAtIHlfdF0kCgojIyMjIEFjY3VyYWN5IGRlbCBmb3JlY2FzdCBjb24gIipzdGwqIiBmdW5jdGlvbgpgYGB7cn0KQ2FtcnlfQlRDX2xvZ19zdGxfcG9pbnRfcHJlZF9hY2N1cmFjeSA8LSBmb3JlY2FzdDo6YWNjdXJhY3koc2FsZXNfbG9nX3N0bF9mb3JbWyJtZWFuIl1dLCBzYWxlc19sb2dfdGVzdCkgCnNob3coQ2FtcnlfQlRDX2xvZ19zdGxfcG9pbnRfcHJlZF9hY2N1cmFjeSkKQ2FtcnlfQlRDX2xvZ19zdGxfcG9pbnRfcmVzaWQgPC0gc2FsZXNfbG9nX3Rlc3Qtc2FsZXNfbG9nX3N0bF9mb3JbWyJtZWFuIl1dCkNhbXJ5X2xvZ19zdGxfcG9pbnRfcHJlZF9NQVNFIDwtIGZhYmxldG9vbHM6Ok1BU0UoQ2FtcnlfQlRDX2xvZ19zdGxfcG9pbnRfcmVzaWQsIHNhbGVzX2xvZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVtZWFuID0gRkFMU0UsIG5hLnJtID0gVFJVRSwgLnBlcmlvZD0xMikKTUFTRV9zdHJpbmcgPC0gc3ByaW50ZigiTUFTRSA9ICVmXG4iLCBDYW1yeV9sb2dfc3RsX3BvaW50X3ByZWRfTUFTRSkKY2F0KE1BU0Vfc3RyaW5nKQpgYGAKIyMjIyBBY2N1cmFjeSBkZWwgZm9yZWNhc3QgY29uICpuYWl2ZSBtZXRob2QqCmBgYHtyfQpDYW1yeV9CVENfbG9nX25haXZlX3BvaW50X3ByZWRfYWNjdXJhY3kgPC0gZm9yZWNhc3Q6OmFjY3VyYWN5KHNhbGVzX2xvZ19uYWl2ZV9wb2ludF9wcmVkLCBzYWxlc19sb2dfdGVzdCkgCnNob3coQ2FtcnlfQlRDX2xvZ19uYWl2ZV9wb2ludF9wcmVkX2FjY3VyYWN5KQpDYW1yeV9CVENfbG9nX25haXZlX3BvaW50X3Jlc2lkIDwtIHNhbGVzX2xvZ190ZXN0LXNhbGVzX2xvZ19uYWl2ZV9wb2ludF9wcmVkCkNhbXJ5X2xvZ19uYWl2ZV9wb2ludF9wcmVkX01BU0UgPC0gZmFibGV0b29sczo6TUFTRShDYW1yeV9CVENfbG9nX25haXZlX3BvaW50X3Jlc2lkLCBzYWxlc19sb2csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbWVhbiA9IEZBTFNFLCBuYS5ybSA9IFRSVUUsIC5wZXJpb2Q9MTIpCk1BU0Vfc3RyaW5nIDwtIHNwcmludGYoIk1BU0UgPSAlZlxuIiwgQ2FtcnlfbG9nX25haXZlX3BvaW50X3ByZWRfTUFTRSkKY2F0KE1BU0Vfc3RyaW5nKQpgYGAKIyMjIyBBY2N1cmFjeSBkZWwgZm9yZWNhc3QgY29uICpIVyBtZXRob2QqCmBgYHtyfQpDYW1yeV9CVENfbG9nX0hXX3BvaW50X3ByZWRfYWNjdXJhY3kgPC0gZm9yZWNhc3Q6OmFjY3VyYWN5KHNhbGVzX2xvZ19IV19wb2ludF9wcmVkLCBzYWxlc19sb2dfdGVzdCkgCnNob3coQ2FtcnlfQlRDX2xvZ19IV19wb2ludF9wcmVkX2FjY3VyYWN5KQpDYW1yeV9CVENfbG9nX0hXX3BvaW50X3Jlc2lkIDwtIHNhbGVzX2xvZ190ZXN0LXNhbGVzX2xvZ19IV19wb2ludF9wcmVkCkNhbXJ5X2xvZ19IV19wb2ludF9wcmVkX01BU0UgPC0gZmFibGV0b29sczo6TUFTRShDYW1yeV9CVENfbG9nX0hXX3BvaW50X3Jlc2lkLCBzYWxlc19sb2csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbWVhbiA9IEZBTFNFLCBuYS5ybSA9IFRSVUUsIC5wZXJpb2Q9MTIpCk1BU0Vfc3RyaW5nIDwtIHNwcmludGYoIk1BU0UgPSAlZlxuIiwgQ2FtcnlfbG9nX0hXX3BvaW50X3ByZWRfTUFTRSkKY2F0KE1BU0Vfc3RyaW5nKQpgYGAKIyMjIyBBY2N1cmFjeSBkZWwgZm9yZWNhc3QgY29uICpHcmFuIG1lYW4gbWV0aG9kKgpgYGB7cn0KQ2FtcnlfQlRDX2xvZ19ncmFuX21lYW5fcG9pbnRfcHJlZF9hY2N1cmFjeSA8LSBmb3JlY2FzdDo6YWNjdXJhY3koc2FsZXNfbG9nX2dyYW5fbWVhbl9wb2ludF9wcmVkLCBzYWxlc19sb2dfdGVzdCkgCnNob3coQ2FtcnlfQlRDX2xvZ19ncmFuX21lYW5fcG9pbnRfcHJlZF9hY2N1cmFjeSkKQ2FtcnlfQlRDX2xvZ19ncmFuX21lYW5fcG9pbnRfcmVzaWQgPC0gc2FsZXNfbG9nX3Rlc3Qtc2FsZXNfbG9nX2dyYW5fbWVhbl9wb2ludF9wcmVkCkNhbXJ5X2xvZ19ncmFuX21lYW5fcG9pbnRfcHJlZF9NQVNFIDwtIGZhYmxldG9vbHM6Ok1BU0UoQ2FtcnlfQlRDX2xvZ19ncmFuX21lYW5fcG9pbnRfcmVzaWQsIHNhbGVzX2xvZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVtZWFuID0gRkFMU0UsIG5hLnJtID0gVFJVRSwgLnBlcmlvZD0xMikKTUFTRV9zdHJpbmcgPC0gc3ByaW50ZigiTUFTRSA9ICVmXG4iLCBDYW1yeV9sb2dfZ3Jhbl9tZWFuX3BvaW50X3ByZWRfTUFTRSkKY2F0KE1BU0Vfc3RyaW5nKQpgYGAKCiMjIyMgQW5hbGlzaSBkZWkgdmFsb3JpIGRpIGFjY3VyYWN5CgpEYWkgdmFsb3JpIG90dGVudXRpIHNpIHBvc3Nvbm8gZWZmZXR0dWFyZSB2YXJpIGNvbmZyb250aToKCi0gSSB2YWxvcmkgZGVsbGEgKipNRSoqIHNvbm8gbWlub3JpIGRpIDAgbmVpIGNhc2kgZGkgKm5haXZlKiBlICpncmFuIG1lYW4qIGUgbWFnZ2lvcmUgZGkgMCBuZWwgY2FzbyBkaSAqc3RsIGZ1bmN0aW9uKiBlICpIVyouIFF1ZXN0byBzdGEgYSBpbmRpY2FyZSBjaGUgaSBwcmltaSBkdWUgdGVuZG9ubyBhIHNvdnJhc3RpbWFyZSBpIHZhbG9yaSwgbWVudHJlIGwndWx0aW1vIHRlbmRlIGEgc290dG9zdGltYXJsaS4gRXNzZW5kbyBpbCB2YWxvcmUgZGkgKkhXKiBpbCBwacO5IGJhc3NvIGluIHZhbG9yZSBhc3NvbHV0byBpbmRpY2EgY2hlIGluIG1lZGlhIHF1ZXN0YSBwcmVkaXppb25lIGhhIGlsIG1pZ2xpb3IgY29tcHJvbWVzc28gdHJhIHNvdnJhc3RpbWEgZSBzb3R0b3N0aW1hLgotIFNlIHNpIGNvbnNpZGVyYSBsYSAqKlJNU0UqKiBpbCB2YWxvcmUgcGnDuSBiYXNzbyDDqCBxdWVsbG8gZGF0byBkYWxsJ3V0aWxpenpvIGRlbCBtZXRvZG8gKm5haXZlKiwgaW5kaWNhbmRvIHVuIG1pZ2xpb3IgcHJlY2lzaW9uZSBkaSBwcmVkaXppb25lIHJpc3BldHRvIGFnbGkgYWx0cmkgbW9kZWxsaSAoYW5jaGUgc2UgaSB2YWxvcmkgZGlmZmVyaXNjb25vIGRpIHBvY28gZWNjZXR0byAqZ3JhbiBtZWFuKikuCi0gSWwgdmFsb3JlIGRlbGxhICoqTUFFKiogw6ggbnVvdmFtZW50ZSBtaW5vcmUgbmVsIGNhc28gZGVsIG1vZGVsbG8gY29uICpuYWl2ZSogc3RhbmRvIGEgaW5kaWNhcmUgbnVvdmFtZW50ZSB1bmEgbWlnbGlvciBwcmVjaXNpb25lLgotIFBlciBxdWFudG8gcmlndWFyZGEgbGEgKipNUEUqKiBpbCB2YWxvcmUgZGkgKkhXKiDDqCAiaWwgcGnDuSB2aWNpbm8iIGEgMC4KLSBMYSAqKk1BUEUqKiDDqCBhbmNoJ2Vzc2EgbWlub3JlIG5lbCBjYXNvICpuYWl2ZSouCi0gSW5maW5lIGkgdmFsb3JpIGRlbGxhICoqTUFTRSoqIChtaW5vcmkgZGkgMSkgaW5kaWNhbm8gY2hlLCB1dGlsaXp6YW5kbyBwZXIgaWwgdHJlbmQgaSBtZXRvZGkgKm5haXZlKiBlICpIVyosIG9wcHVyZSBsYSBmdW56aW9uZSBkaSBmb3JlY2FzdCBhdXRvbWF0aWNvICpzdGwqIHNpIG90dGVuZ29ubyBwcmVkaXppb25pIG1pZ2xpb3JpIHJpc3BldHRvIGFsIG1vZGVsbG8gZGkgcHJlZGl6aW9uZSBiYW5hbGUgKCpuYWl2ZSopIHBlciB0dXR0YSBsYSBUaW1lIFNlcmllcy4gTmVsIGNhc28gZGVsbGEgKkdyYW4gTWVhbiogc2kgb3R0aWVuZSB1bmEgKk1BU0U+MSogaW5kaWNhbmRvIGVycm9yaSBkaSBwcmVkaXppb25lIHBlZ2dpb3JpIGRpIHF1ZWxsaSBkZWxsYSBwcmVkaXppb25lIGJhbmFsZS4KCkNvbWUgY29uY2x1c2lvbmUgZ2VuZXJhbGUgc2kgcHXDsiBkaXJlIGNoZSBpbCBtb2RlbGxvIGNoZSB1dGlsaXp6YSBpbCAqbmFpdmUgbWV0aG9kKiBwZXIgaWwgZm9yZWNhc3QgZGVsIHRyZW5kIMOoIGlsIHBpw7kgYWNjdXJhdG8uCgpJbmZpbmUgc2kgc2FsdmFubyBnbGkgZXJyb3JpIGRlcml2YXRpIHNpYSBkYWwgKmZpdHRpbmcqIGRlaSBkYXRpIGRlbCAqVHJhaW5zZXQqIHNpYSBkYWxsYSAqcHJlZGljdGlvbiogZGVpIGRhdGkgZGVsICpUZXN0c2V0KiBwZXIgcG90ZXIgY29tcGFyYXJlLCB0cmFtaXRlIGlsIHRlc3QgZGkgKkRpZWJvbGQtTWFyaWFubyosIGlsIG1vZGVsbG8gYXR0dWFsZSBjb24gcXVlbGxvIGNoZSBzaSBwcm92ZXLDoCBhIHV0aWxpenphcmUgaW4gc2VndWl0by4KYGBge3J9CndyaXRlLmNzdihDYW1yeV9Ucm5TX2RmLCAiQ2FtcnlfVVNfc2FsZXNfdHJhaW5zZXQuY3N2IikKc2FsZXNfbG9nX25haXZlX3BvaW50X3ByZWRfZGYgPSBhcy5kYXRhLmZyYW1lKHNhbGVzX2xvZ19uYWl2ZV9wb2ludF9wcmVkKQpDYW1yeV9CVENfbG9nX25haXZlX3BvaW50X3Jlc2lkX2RmID0gYXMuZGF0YS5mcmFtZShDYW1yeV9CVENfbG9nX25haXZlX3BvaW50X3Jlc2lkKQp3cml0ZS5jc3Yoc2FsZXNfbG9nX25haXZlX3BvaW50X3ByZWRfZGYsICJzYWxlc19sb2dfc3RsX3BvaW50X3ByZWRfZGYiKQp3cml0ZS5jc3YoQ2FtcnlfQlRDX2xvZ19uYWl2ZV9wb2ludF9yZXNpZF9kZiwgIkNhbXJ5X0JUQ19sb2dfc3RsX3BvaW50X3Jlc2lkX2RmLmNzdiIpCmBgYAoKCgoK